VACUUM 被 xmin 水位线阻塞
一个事务中的 MVCC 快照,存储了有关特定时刻事务 ID 可见性的信息。快照中会有一个名为xmin的组件,它表示了仍处于活动状态的最低事务 ID。所有小于xmin的事务 ID 要么提交且可见,要么回滚已死亡。VACUUM 可能会被xmin水位线阻塞。
xmin水位线告诉您 VACUUM 进程可以清理死行的最高水位。当这个值落后且不往前推进时,VACUUM 将被阻塞,并且不能清理死行。
当 VACUUM 被阻塞并且死行不能被清理时,它会导致表膨胀和查询速度变慢。
xmin水位线是所有数据库中最老的xmin,这汇总为一个服务器范围的指标。请注意,虽然某些类型的xmin水位线延迟,会影响服务器上的所有数据库,例如每个物理复制槽的xmin,但在另外一些情况下,并非服务器上的所有数据库都会受到影响,因为 PostgreSQL 仍然可以清理不相关数据库中表的死行。例如,如果xmin水位线被 1 号数据库上的长时间运行的事务所阻塞,PostgreSQL 仍然可以清理 2 号数据库中表的死行,因为 1 号数据库的事务永远不可能看到 2 号数据库。
滞后或过时的物理复制槽
在启用物理流复制并设置hot_standby_feedback时,如果复制滞后或副本服务器异常(例如,停机),则复制槽需要数据库保留的最老事务可能会“卡住”,从而阻碍了xmin水位线的推进。
一个复制槽是一种数据结构,它可以防止 PostgreSQL 服务器丢弃备用服务器仍然需要的信息,以让备用服务器赶上主服务器。如果复制延迟或备用服务器停机,则复制槽将阻止VACUUM删除旧行。
方案
您可以使用下面查询,查找所有复制槽及其xmin值:
SELECT slot_name, database, xmin
FROM pg_replication_slots
WHERE slot_type = 'physical'
ORDER BY age(xmin) DESC;
如果不再使用复制槽,可以通过运行下面命令将其删除:
SELECT pg_drop_replication_slot('<slot_name>');
滞后或过时的逻辑复制槽
使用逻辑复制槽时,当 DDL 更改(数据库迁移)未应用至一个副本服务器(订阅服务器)时,复制也会过时。当订阅端由于表结构不匹配而无法复制数据时,复制会出错并过时。这会导致主服务器(发布端)的系统表的xmin水位线停止推进,直到复制恢复。
方案
您可以使用下面查询,查找所有复制槽及其catalog_xmin值:
SELECT slot_name, database, catalog_xmin
FROM pg_replication_slots
WHERE slot_type = 'logical'
ORDER BY age(catalog_xmin) DESC;
您可能还需要检查发布端和订阅端服务器上的日志,以了解有关逻辑复制的任何错误消息,例如表结构差异或wal_sender_timeout/wal_receiver_timeout设置值过低,导致复制进程在一段时间后退出,而没有往前推进。
如果不再使用复制槽,可以通过运行下面命令将其删除:
SELECT pg_drop_replication_slot('<slot_name>');