向表中添加可为空列的成本超过 10 分钟

Mat*_*hia 11 postgresql database-design alter-table postgresql-9.1

我在表上添加新列时遇到问题。
我尝试运行了几次,但是运行了 10 多分钟后,由于锁定时间,我决定取消查询。

ALTER TABLE mytable ADD mycolumn VARCHAR(50);
Run Code Online (Sandbox Code Playgroud)

有用的信息:

  • PostgreSQL 版本:9.1
  • 行数:~ 250K
  • 列数:38
  • 可空列数:32
  • 约束数量:5(1 PK、3 FK、1 UNIQUE)
  • 索引数:1
  • 操作系统类型:Debian Squeeze 64

我发现了有关 PostgreSQL 管理可空列的方式的有趣信息(通过 HeapTupleHeader)。

我的第一个猜测是,因为这个表已经有 32 个 8 位的可空列MAXALIGN, HeapTupleHeader 是 4 字节长度(未验证,我不知道如何这样做)。

因此,添加新的可为空的列可能需要在每一行上更新 HeapTupleHeader 以添加新的 8-bits MAXALIGN,这可能会导致性能问题。

因此,我尝试更改可空列之一(实际上并不是真正可以为空的),以便将可空列的数量减少到 31,以检查我的猜测是否属实。

ALTER TABLE mytable ALTER myothercolumn SET NOT NULL;
Run Code Online (Sandbox Code Playgroud)

不幸的是,这个改动也需要很长时间,超过5分钟,所以我也中止了。

您知道是什么导致了这种性能成本吗?

Erw*_*ter 8

这里有几个误解:

空位图堆元组报头的一部分。根据文档:

有一个固定大小的头(在大多数机器上占用 23 个字节),后跟一个可选的空位图......

由于两个原因,您的 32 个可空列是不可疑的:

  • 每行添加空位图,并且仅当该行中至少有一个实际NULL时。可空列没有直接影响,只有实际NULL值有影响。如果分配了空位图,则它总是完全分配(全部或全部)。空位图的实际大小是每列 1 位,四舍五入到下一个字节每个当前源代码:

    #define BITMAPLEN(NATTS) (((int)(NATTS) + 7) / 8)
    
    Run Code Online (Sandbox Code Playgroud)
  • 空位图在堆元组头之后分配,后跟可选的 OID,然后是行数据。OID 或行数据的开始由t_hoff标头中的表示。每个评论源代码

    请注意,t_hoff 必须是 MAXALIGN 的倍数。

  • heap tuple header后面有1个空闲字节,占23个字节。因此,最多8列的行的空位图有效地无需额外成本。对于表中的第 9 列,将增加t_hoff另一个MAXALIGN(通常为 8 个)字节以提供另外 64 列。所以下一个边界是72列。

要显示 PostgreSQL 数据库集群(包括MAXALIGN)的控制信息,以在 Debian 机器上典型安装 Postgres 9.3 的示例:

    sudo /usr/lib/postgresql/9.3/bin/pg_controldata /var/lib/postgresql/9.3/main
Run Code Online (Sandbox Code Playgroud)

我更新了 了您引用的相关答案中的说明

除此之外,即使您的ALTER TABLE语句触发了整个表的重写(它可能会更改数据类型),250K 确实不是那么多,并且在任何中途体面的机器上只需几秒钟(除非行异常大) . 10 分钟或更长时间表示完全不同的问题。您的语句很可能正在等待获得表上的锁定。

越来越多的条目 pg_stat_activity意味着更多的开放事务 - 表明对表的并发访问(最有可能)必须等待操作完成。

黑暗中拍了几张

检查可能的表膨胀,尝试温和的VACUUM mytable或更积极的VACUUM FULL mytable- 这可能会遇到相同的并发问题,因为这种形式也获得了排他锁。你可以试试pg_repack代替...

我将首先检查索引、触发器、外键或其他约束的可能问题,尤其是那些涉及列的问题。特别是可能涉及损坏的索引?尝试REINDEX TABLE mytable;DROP所有这些,然后ALTER TABLE 在同一事务中重新添加它们。

尝试在夜间或负载不大的时候运行该命令。

一种蛮力方法是停止对服务器的访问,然后再试一次:

如果无法确定,升级到当前版本或即将推出的 9.4可能会有所帮助。对大表和锁定细节进行了多项改进。但是,如果您的数据库中出现问题,您可能应该先弄清楚。

  • 几乎可以肯定是锁。但是,作为测试,您始终可以创建表的副本并尝试更改它。如果这不需要很长时间,那么您就会知道问题不是实际的修改。 (2认同)