【问题标题】:Make a column NOT NULL in a large table without locking issues?在没有锁定问题的情况下在大表中创建 NOT NULL 列?
【发布时间】:2017-02-06 14:51:07
【问题描述】:

我想将一列更改为 NOT NULL:

ALTER TABLE "foos" ALTER "bar_id" SET NOT NULL

“foos”表有近 1 000 000 条记录。它的写入量相当低,但相当稳定。阅读量很大。

根据我的经验,像这样将大表中的列更改为 NOT NULL 可能会导致应用停机,大概是因为它会导致 (b) 锁定。

不过,我还没有找到一个很好的解释来证实这一点。

如果是真的,我该怎么做才能避免呢?

编辑:The docs(通过this comment)说:

使用 DEFAULT 子句添加列或更改现有列的类型将需要重写整个表及其索引。

我不确定更改 NULL 是否算作“更改现有列的类型”,但我相信上次看到此问题时我确实在该列上有了索引。

也许删除索引,使列 NOT NULL,然后添加索引会有所改善?

【问题讨论】:

  • 在 DBA Stack Exchange 上找到这个:dba.stackexchange.com/questions/66840/… 在我写这篇文章的时候,他们基本上是说没有办法,尽管你可以做其他事情,比如使用 CONSTRAINTs(也建议@a_horse_with_no_name 下面)。
  • 看起来其中一个答案由于某种原因被删除了。它指出了the docs 如何说“除非明确指出,否则会持有访问独占锁”。 (并且没有明确指出其他任何内容。)据我所知,这是一个读取和写入的全表锁。该答案还表示将对整个表进行顺序扫描,以确保所有列均为 NULL。我猜这两件事(慢扫描、全表锁)一起解释了问题。
  • 在 postgres 11 中这个 cnstraint 已被删除。
  • @dland 谢谢!您的意思是 CONSTRAINT 解决方案不再可能,或者更改为 NOT NULL 不再危险?编辑:可能是后者? “...... PostgreSQL 将重写整个表,这在活动系统中的较大表上可能会导致一系列问题。PostgreSQL 11 在大多数情况下消除了重写表的需要,因此运行 ALTER TABLE .. ADD COLUMN .. DEFAULT 。 . 将非常迅速地执行。” postgresql.org/about/news/1855
  • 是的,在第 11 页中,SET NOT NULL 不再需要重写表。如果在指定 NOT NULL 时查询“空”列,引擎将从新系统目录中提取缺少的默认值,从而遵守非空约束。以前,必须用新列上的默认值填充该行,因为没有回退到其他地方查看。他们基本上解决了另一个间接级别的问题。

标签: postgresql


【解决方案1】:

我认为您可以使用检查约束而不是 set not null 来做到这一点。

ALTER TABLE foos 
     add constraint id_not_null check (bar_id is not null) not valid;

这仍然需要表上的 ACCESS EXCLUSIVE 锁,但它非常快,因为 Postgres 不验证约束(因此它不必扫描整个表)。这将确保新行(或更改的行)不能将空值放入该列

然后(在提交 alter table! 之后)你可以这样做:

alter table foos validate constraint id_not_null;

需要 ACCESS EXCLUSIVE 锁并且仍然允许访问表。

【讨论】:

  • 谢谢。我刚刚在另一个 Stack Exchange 上偶然发现了一个姐妹问题,他们也在那里 mention this - 似乎是一个合理的解决方法,如果一个可以混合 NOT NULL 和 CONSTRAINTs 的话。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-09
  • 1970-01-01
  • 2018-03-23
  • 2020-06-28
  • 1970-01-01
  • 2022-01-17
相关资源
最近更新 更多