Postgres XID 回卷 / autovacuum 危机
症状
出现 'database is not accepting commands to avoid wraparound data loss';autovacuum 跟不上,datfrozenxid 不断老化。
1. 理解紧迫性
PostgreSQL 事务 ID 是 32 位的;接近耗尽时,服务器会停止写入以防止数据丢失。这是一种关键且时间紧迫的情况。好消息是:它可以解决;需要的是有条不紊,而不是惊慌。
2. 测量离上限有多近
取出每个数据库中最老的未冻结 XID 的年龄;它与 autovacuum_freeze_max_age(默认 200M)的接近程度反映了危险程度。
PostgreSQL
SELECT datname, age(datfrozenxid) AS xid_age
FROM pg_database ORDER BY xid_age DESC;3. 找出最老的表
问题通常来自少数几张大表/频繁写入的表;集中关注 relfrozenxid 最老的值。
PostgreSQL
SELECT relname, age(relfrozenxid) AS xid_age
FROM pg_class WHERE relkind IN ('r','m','t')
ORDER BY xid_age DESC LIMIT 20;4. 清除阻碍 VACUUM 的因素
长时间打开的事务、废弃的复制槽和预备事务会阻碍清理(冻结)。找出并解决它们——否则无论 VACUUM 运行多久都不会有进展。
PostgreSQL
SELECT pid, now()-xact_start AS age, state FROM pg_stat_activity WHERE xact_start IS NOT NULL ORDER BY age DESC;
SELECT slot_name, active FROM pg_replication_slots WHERE NOT active;
SELECT * FROM pg_prepared_xacts;5. 执行激进的 FREEZE
阻碍因素清除后,从最老的表开始强制冻结。即使服务器已停止分配新的事务 ID,也不要切换到单用户模式(postgres --single):当前的 PostgreSQL 文档指出已不再需要且应避免使用。以超级用户身份在正常模式下连接并运行 VACUUM 就足够了;在 PG14+ 中,接近回卷时 VACUUM 的 failsafe 机制会启动。
PostgreSQL
VACUUM (FREEZE, VERBOSE) oldest_table;
-- Entire database: vacuumdb --all --freeze --jobs=46. 永久措施:强化并监控 autovacuum
提高 autovacuum_vacuum_cost_limit,降低 autovacuum_naptime,并为写入频繁的表设置表级存储参数。为 age(datfrozenxid) 设置告警;再也不要经历这种危机。