【问题标题】:PostgreSQL ADD COLUMN DEFAULT NULL locks and performancePostgreSQL ADD COLUMN DEFAULT NULL 锁和性能
【发布时间】:2018-05-22 10:02:12
【问题描述】:

我的 PostgreSQL 9.6 数据库中有一个包含 300 万行的表。此表已经有一个空位图(它有 2 个其他 DEFAULT NULL 字段)。我想向该表添加一个新的布尔可空列。我坚持这两种说法之间的区别:

ALTER TABLE my_table ADD COLUMN my_column BOOLEAN;
ALTER TABLE my_table ADD COLUMN my_column BOOLEAN DEFAULT NULL;

我认为这些说法没有区别,但是:

  1. 我在文档中找不到任何证据。文档告诉我们,为新列提供 DEFAULT 值会使 PostgreSQL 重写所有元组,但我认为这种情况下不正确,因为默认值是 NULL
  2. 我在这个表的副本上运行了一些测试,第一个语句(没有DEFAULT NULL)比第二个语句花费了更多的时间。我不明白为什么。

我的问题是:

  1. PostgreSQL 会为这两个语句使用相同的锁类型 (ACCESS EXCLUSIVE) 吗?
  2. 如果我使用DEFAULT NULL,PostgreSQL 是否会重写所有元组以将NULL 值添加到每个元组?
  3. 这两种说法有区别吗?

【问题讨论】:

标签: postgresql


【解决方案1】:

https://www.postgresql.org/docs/current/static/sql-altertable.html

  1. 是的 - 相同的 ACCESS EXCLUSIVE,默认为 NULL 或未提及默认值没有例外(我认为统计、“选项”、约束、集群要求不那么严格,但不添加列)

请注意,每个子表单所需的锁定级别可能不同。一个 除非明确指出,否则会持有 ACCESS EXCLUSIVE 锁。当多个 列出子命令,持有的锁将是最严格的 任何子命令都需要。

  1. 不 - 它宁愿将 NULL 附加到选择结果中

当使用 ADD COLUMN 添加列时,表中的所有现有行 使用列的默认值初始化(如果没有 DEFAULT,则为 NULL 条款被指定)。如果没有 DEFAULT 子句,这只是一个 元数据更改,并且不需要立即更新 表的数据;而是在读出时提供添加的 NULL 值。

  1. 不 - AFAIK 没有区别。只是元数据在这两种情况下都会发生变化(我相信这是一种用不同语义表达的情况)

编辑 - 演示:

db=# create table so(i int);
CREATE TABLE
Time: 9.498 ms
db=# insert into so select generate_series(1,10*1000*1000);
INSERT 0 10000000
Time: 13899.190 ms
db=# alter table so add column nd BOOLEAN;
ALTER TABLE
Time: 1025.178 ms
db=# alter table so add column dn BOOLEAN default null;
ALTER TABLE
Time: 13.849 ms
db=# alter table so add column dnn BOOLEAN default true;
ALTER TABLE
Time: 14988.450 ms
db=# select version();
                                                    version
----------------------------------------------------------------------------------------------------------------
 PostgreSQL 9.6.1 on x86_64-apple-darwin15.6.0, compiled by Apple LLVM version 8.0.0 (clang-800.0.42.1), 64-bit
(1 row)

最后,为了避免猜测,它是特定于数据类型的:

db=# alter table so add column t text;
ALTER TABLE
Time: 25.831 ms
db=# alter table so add column tn text default null;
ALTER TABLE
Time: 13.798 ms
db=# alter table so add column tnn text default 'null';
ALTER TABLE
Time: 15440.318 ms

【讨论】:

    【解决方案2】:

    Vao Tsun 在第 2 点的回应中存在问题。

    如果你使用ALTER TABLE my_table ADD COLUMN my_column BOOLEAN;,它不会重写所有的元组,它只是元数据的变化。

    但是如果你使用ALTER TABLE my_table ADD COLUMN my_column BOOLEAN DEFAULT NULL,它会重写所有的元组,并且它会在长表上永远存在。

    文档本身就说明了这一点。

    当使用 ADD COLUMN 添加列时,表中的所有现有行都使用该列的默认值初始化(如果未指定 DEFAULT 子句,则为 NULL)。 如果没有 DEFAULT 子句,这只是元数据更改,不需要立即更新表数据;而是在读出时提供添加的 NULL 值。

    这告诉我们如果有DEFAULT子句,即使是NULL,也会重写所有的元组。

    这是由于更新子句的性能问题。如果您需要对未重写的元组进行更新,则需要将元组移动到另一个磁盘空间,从而消耗更多时间。

    我自己在 Postgresql 9.6 上对此进行了测试,当时我不得不在有 300+ 百万元组的表上添加一列。没有DEFAULT NULL 持续了11 毫秒,而DEFAULT NULL 持续了30 多分钟。

    【讨论】:

    • 请将您的测试用例添加到答案中 - 我通过引用 /timing 的语句来确定我的答案。也请在您的完整构建版本中添加它。
    • 请注意,文档的 current 版本 (11) 表示“在这两种情况下都不需要重写表”。假设默认值是非易失性的(易失性是 random() 之类的东西,这将是一个愚蠢的默认值)。所以这仅适用于旧版本。
    • 这里的结论并不准确。 DEFAULT NULL 和没有 DEFAULT 之间没有区别。请参阅@vau-tsun 的回答,解释得很好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-03
    • 2018-11-06
    • 2017-03-19
    • 2022-12-01
    • 2011-12-16
    • 1970-01-01
    相关资源
    最近更新 更多