【问题标题】:ADD COLUMN with DEFAULT value to a huge table将具有默认值的列添加到一个巨大的表中
【发布时间】:2012-12-08 11:35:29
【问题描述】:

我有一个 postgresql 数据库和一个包含近十亿行的表。 当我尝试添加具有默认值的新列时:

ALTER TABLE big_table
ADD COLUMN some_flag integer NOT NULL DEFAULT 0;

事务持续了 30 多分钟......并且数据库日志开始发出警告。

有什么方法可以优化查询?

【问题讨论】:

  • 您在日志文件中看到了哪些警告?
  • 您可以使用其数据创建表的副本,将列添加到其中,然后通过重命名两个表来交换它们。 (您可能必须适当地处理依赖关系。)
  • @dezso 我们正在讨论 10 亿行繁忙的表...如何复制它以使过程更快/更好
  • @d.raev dezso 可能是正确的 (see my answer),因为 Postgres WAL 的性质。

标签: postgresql


【解决方案1】:

除了分批做(这还需要一段时间):

您可以将表转储为 COPY 语句并编写脚本来编辑 COPY 语句的内容以插入另一列(COPY 可以是 CSV IIRC)。

然后您只需重新加载更改后的COPY 转储,理论上它应该比ALTER 更快,因为 COPY 不会记录事务。

另一种选择是在运行命令时关闭fsync...只要记住将其重新打开即可。

你也可以分批做以上两个。

【讨论】:

  • 谢谢,我希望有更多的查询构造技巧,但似乎没有。下次我会考虑Fsync。
  • 归根结底,十亿就是十亿。这不是一个小数字:)。我很好奇数据是什么?
  • 它表示来自 RL 文档的解析数据。当然有更好的存储概念,但改变整个结构不是一种选择。
  • 不是对实施选择的批评。我只是好奇,因为我一直对人们如何在“现实世界”中使用东西感兴趣。
【解决方案2】:

从 PostgreSQL 11 开始,这种行为将会改变。

Waiting for PostgreSQL 11 – Fast ALTER TABLE ADD COLUMN with a non-NULL default:

所以,在最长的时间里,你是什么时候这样做的:

alter table x add column z text;

几乎是瞬间完成的。锁定表,将有关新列的信息添加到系统目录中,就完成了。

但是当你尝试时:

alter table x add column z text default 'some value';

然后花了很长时间。需要多长时间取决于桌子的大小。

这是因为 postgresql 实际上是在重写整个表,将列添加到每一行,并用默认值填充。

“如果您还想将列设置为 NOT NULL,会发生什么情况?在这种情况下,我们是回到慢速版本还是同样处理?”

not null 不会改变任何东西。它是新行的约束。所以添加一个带有“not null default ‘xxx’”的列会很快。

【讨论】:

    【解决方案3】:

    我会考虑在不使用默认值的情况下创建列,并通过间歇性提交手动更新行以应用默认值。

    【讨论】:

    • 本来也是这么想的,但最后可能需要更多时间。我需要把它放在“版本更新”事务中......所以我正在寻找一个更清洁的解决方案。
    • 需要复制所有行以将非空值复制到新列中。通过在ALTER TABLE 中指定默认值,您可以一次完成所有操作,同时阻止其他访问。您可以在其他进程访问表时增量地执行此操作,方法是将列添加为可空值并执行一系列小更新,可能在迭代之间休眠。不要在一个大的UPDATE 中为整张桌子这样做,否则你会膨胀桌子。
    • 为了最有效,您还需要在每批之间进行 VACUUM。
    猜你喜欢
    • 2021-12-11
    • 2010-09-10
    • 1970-01-01
    • 2020-04-23
    • 1970-01-01
    • 1970-01-01
    • 2022-01-13
    • 2018-07-31
    相关资源
    最近更新 更多