如何在表扫描期间不锁定表的情况下将列设置为 NOT NULL?

Joh*_*hir 5 postgresql index null alter-table locking

(前一个问题是:将多个列设置为非空时,Postgres 会使用多列索引吗?)


通常,当我将列设置为非空时,如果它没有索引,那么我首先添加它,以便 postgres 可以(希望)在锁定表的同时进行表扫描时使用索引,以便表被锁定在较短的时间内。

我想设置几列不为空,如下所示:

alter table foos
  alter column bar1 set not null
  alter column bar2 set not null
  alter column bar3 set not null
  alter column bar4 set not null;
Run Code Online (Sandbox Code Playgroud)

如果我为这些列创建了多列索引,那么在进行此更改之前,postgres 会在扫描锁定表时使用它吗?

CREATE INDEX CONCURRENTLY my_index on foos (bar1, bar2, bar3, bar4);
Run Code Online (Sandbox Code Playgroud)

如果我在 IS NULL(或 IS NOT NULL)上建立了部分索引怎么办

CREATE INDEX CONCURRENTLY my_index on foos (bar1, bar2, bar3, bar4) where bar1 is null and bar2 is null and bar3 is null and bar4 is null;
Run Code Online (Sandbox Code Playgroud)

Mel*_*kij 9

另一个 postgresql 贡献者的另一个答案。

在执行“alter table set not null”期间,PostgreSQL 甚至不会尝试使用任何索引。它只是没有实施。

正确实施索引扫描是困难的部分。我们不能只做这样的查询

select exists(select from foos where bar1 is null)
Run Code Online (Sandbox Code Playgroud)

由于各种原因,来自 alter table 命令。此类功能将需要大量代码(并且可能在某些边缘情况下需要脆弱的代码)、大量工作,仅适用于有限的用例。开发人员不喜欢的东西。实际上,pgsql-hackers 社区不喜欢 NOT NULL 存储在系统目录中的方式。重新设计目录的这一部分会更清晰。之后,可以使用短锁执行 SET NOT NULL NOT VALID 和没有排他锁的表验证。类似于alter table .. add constraint ... not valid+alter table .. validate constraint用于检查约束或外键。但是这样的重新设计工作量很大,而且没有人愿意去做。

但我有个好消息:在 PostgreSQL 12(及更高版本)中,扫描整个表并不是唯一的选择。alter table set not null可以通过现有的检查约束证明 NOT NULL 的正确性。所以,你可以这样做:

-- short-time exclusive lock
alter table foos 
  add constraint foos_not_null 
  check (bar1 is not null) not valid;

-- seqscan, but without exclusive lock, concurrent sessions can read/write
alter table foos validate constraint foos_not_null;

-- exclusive lock, but foos_not_null proves: there is no NULL in this column
-- so this alter table would be fast
alter table foos alter column bar1 set not null;
-- not needed anymore
alter table foos drop constraint foos_not_null;
Run Code Online (Sandbox Code Playgroud)

那是我的补丁。是的,这看起来像是一种解决方法。但是,幸运的是,它被合并了,现在可以set not null不用长时间的排他锁了。