PostgreSQL ADD COLUMN DEFAULT NULL锁定和性能

Iva*_*ita 13 postgresql

我的PostgreSQL 9.6数据库中有一个表,有300万行.该表已经有一个空位图(它有另外两个DEFAULT NULL字段).我想在这个表中添加一个新的布尔可空列.我坚持这两个陈述之间的区别:

ALTER TABLE my_table ADD COLUMN my_column BOOLEAN;
ALTER TABLE my_table ADD COLUMN my_column BOOLEAN DEFAULT NULL;
Run Code Online (Sandbox Code Playgroud)

我认为这些陈述没有区别,但是:

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

我的问题是:

  1. PostgreSQL会对ACCESS EXCLUSIVE这两个语句使用相同的锁类型()吗?
  2. PostgreSQL会重写所有元组NULL,以便在我使用的情况下为每个元组增加价值DEFAULT NULL吗?
  3. 这两个陈述之间有什么区别吗?

小智 20

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上测试了这个,当时我不得不在一个有3亿多元组的表上添加一列.没有DEFAULT NULL它持续了11毫秒,并DEFAULT NULL持续了超过30分钟.

  • 请注意,文档的_current_版本(11)表示“在任何情况下都不需要重写表”。假设默认值是非易失性的(volatile将是random()之类的东西,这将是一个愚蠢的默认值)。因此这仅适用于旧版本。 (2认同)
  • 请参阅 https://www.postgresql.org/docs/11/sql-altertable.html 的“注释”部分 (2认同)
  • 这里的结论不准确。DEFAULT NULL和DEFAULT缺少之间没有区别。参见@ vau-tsun的答案,解释得很好。 (2认同)
  • 版本 10 和版本 11 之间的文档注释存在重要差异!版本 11:“在这两种情况下都不需要重写表。”版本 10:“如果没有 DEFAULT 子句,则这只是元数据更改,不需要立即更新表的数据;相反,添加的 NULL 值是在读出时提供的。 (2认同)

Vao*_*sun 6

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

  1. Yes - same ACCESS EXCLUSIVE, no exceptions for DEFAULT NULL or no DEFAULT mentionned (statistics, "options", constraints, cluster would require less strict I think, but not add column)

Note that the lock level required may differ for each subform. An ACCESS EXCLUSIVE lock is held unless explicitly noted. When multiple subcommands are listed, the lock held will be the strictest one required from any subcommand.

  1. No - it will rather append NULL to result on select

When a column is added with ADD COLUMN, all existing rows in the table are initialized with the column's default value (NULL if no DEFAULT clause is specified). If there is no DEFAULT clause, this is merely a metadata change and does not require any immediate update of the table's data; the added NULL values are supplied on readout, instead.

  1. No - no difference AFAIK. Just metadata change in both cases (as I believe it is one case expressed with different semantics)

Edit - Demo:

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)
Run Code Online (Sandbox Code Playgroud)

lastly to avoid speculations it is data type specific:

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
Run Code Online (Sandbox Code Playgroud)