以最小的影响将外键更改为 ON DELETE CASCADE

The*_*Sky 9 postgresql foreign-key postgresql-9.5 online-operations

我有一个已ON DELETE NO ACTION定义的现有外键。我需要将此外键更改为ON DELETE CASCADE. 我可以在交易中做到这一点:

begin;
alter table posts drop constraint posts_blog_id_fkey;
alter table posts add constraint posts_blog_id_fkey foreign key (blog_id) references blogs (id) on update no action on delete cascade;
commit;
Run Code Online (Sandbox Code Playgroud)

问题是该posts表很大(400 万行),这意味着验证外键可能需要很长的时间(我已经用数据库的副本对此进行了测试)。滴/添加外键获取的ACCESS EXCLUSIVEposts。因此,添加外键会在相当长的时间内阻止对表的所有访问,posts因为在发生约束验证时会持有锁。我需要执行在线迁移(我没有专门的停机时间窗口)。

我知道我可以执行2 个事务来帮助检查需要很长时间:

begin;
alter table posts drop constraint posts_blog_id_fkey;
alter table posts add constraint posts_blog_id_fkey foreign key (blog_id) references blogs (id) on update no action on delete cascade not valid;
commit;

begin;
alter table posts validate constraint posts;
commit;
Run Code Online (Sandbox Code Playgroud)

这种方法的优点是ACCESS EXCLUSIVE锁被维持了很短的时间下降/加约束,然后验证约束只有一个SHARE UPDATE EXCLUSIVEpostsROW SHARE上锁定blogs,因为我对Postgres的9.5。

这有什么缺点吗?我知道添加NOT VALID到约束意味着不验证现有数据,但VALIDATE CONSTRAINT 检查之前插入/更新的任何行。因为在同一个事务中删除/添加外键,是否有可能创建不一致的数据?

Eva*_*oll 7

文档是这样说的 NOT VALID

ADD table_constraint [ NOT VALID ]
Run Code Online (Sandbox Code Playgroud)

这种形式使用与 相同的语法向表添加一个新约束CREATE TABLE加上选项NOT VALID,目前仅允许用于外键和 CHECK 约束。如果约束被标记为NOT VALID,则跳过可能冗长的初始检查以验证表中的所有行是否满足约束。该约束仍将针对后续插入或更新强制执行(也就是说,除非引用表中有匹配的行,否则它们将失败,在外键的情况下;除非新行与指定的检查匹配,否则它们将失败约束)。但是数据库不会假设约束对表中的所有行都成立,直到使用该VALIDATE CONSTRAINT选项对其进行验证。

你所关心的,

我知道添加NOT VALID到约束意味着不验证现有数据,但VALIDATE CONSTRAINT将检查之前插入/更新的任何行。

他们只会在验证过程中进行检查你告诉它来验证,所以您需要推迟,直到你预定的停机时间。

因为在同一个事务中删除/添加外键,是否有可能创建不一致的数据?

不,因为您添加的第二个NOT VALID它适用于在语句之后插入的所有行,就好像它们总是在那里一样。VALIDATION 用于在FOREIGN KEY引用的行不存在时拒绝创建。它与级联无关,请注意

CREATE TABLE foo
AS
  SELECT 1 AS a;

CREATE TABLE bar
AS
  SELECT a
  FROM ( VALUES (1),(2) )
    AS t(a);

ALTER TABLE foo
  ADD PRIMARY KEY (a);

ALTER TABLE bar
  ADD FOREIGN KEY (a)
  REFERENCES foo
  ON DELETE CASCADE
  NOT VALID;

DELETE FROM foo;


TABLE foo;
 a 
---
(0 rows)

test=# TABLE bar;
 a 
---
 2
(1 row)
Run Code Online (Sandbox Code Playgroud)

此时你可以看到

  1. bar级联到删除bar
  2. 栏上缺乏验证意味着它仍然有一行无效

约束仍然无法验证(如下所示),但对于级联删除的目的,一切都很好。

ALTER TABLE bar VALIDATE CONSTRAINT bar_a_fkey  ;
ERROR:  insert or update on table "bar" violates foreign key constraint "bar_a_fkey"
DETAIL:  Key (a)=(2) is not present in table "foo".
Run Code Online (Sandbox Code Playgroud)

顺便说一句你可以写这个

begin;
alter table posts drop constraint posts_blog_id_fkey;
alter table posts add constraint posts_blog_id_fkey foreign key (blog_id) references blogs (id) on update no action on delete cascade not valid;
commit;
Run Code Online (Sandbox Code Playgroud)

像这样

alter table posts
  drop constraint posts_blog_id_fkey,
  add constraint posts_blog_id_fkey
    foreign key (blog_id)
    references blogs (id)
    on update no action
    on delete cascade
    not valid;
Run Code Online (Sandbox Code Playgroud)

您不必将其包装在 txn 中。您也不必在 txn 中包装任何单个语句——PostgreSQL 不是 MySQL。一切都已经是事务性的。