近期,在一次客户的数据库业务场景测试中,一条报错打破了平静。
ERROR: 54000: database is not accepting commands
to avoid wraparound data loss in database "template1"数据库突然拒绝写入。而就在 18 分钟前,checkpoint 还一切正常地完成了 27 万个 buffer 的落盘。
从「一切正常」到「全线崩溃」,只用了 18 分钟。
一句话总结:
autovacuum_naptime = 23h等于关闭了 autovacuum。这不是一次意外,而是一颗埋了整整 23 小时的定时炸弹。
事故时间线
把时间轴拉出来,整个过程快得让人来不及反应:
但真正的危机,早在 23 小时之前就已经埋下——autovacuum 已经 23 小时没有启动过了。

根因定位:一个参数引发的问题
排查下来,问题浓缩成一行配置:
SHOW autovacuum_naptime; -- 1380min = 23 小时这个参数的默认值只有 1 分钟,却被调成了 23 小时。也就是说,autovacuum 清理进程每 23 小时才被唤醒一次。
参数一调,连锁反应就来了:
恶性循环,一环扣一环:

为什么「小事务」是帮凶
很多人以为只有大事务才消耗资源。恰恰相反——每个事务,无论多小,都要消耗 1 个 XID。大量小事务,就是 XID 的「收割机」。
# 危险:1000万个小事务 = 1000万个 XID
for i in range(10000000):
cursor.execute("BEGIN")
cursor.execute("UPDATE table SET ... WHERE id = %s", (i,))
cursor.execute("COMMIT")
# 安全:1 个大事务 = 1 个 XID
cursor.execute("BEGIN")
cursor.execute("UPDATE table SET ... WHERE id BETWEEN 1 AND 10000000")
cursor.execute("COMMIT")这些场景最容易中招,看看有没有踩过:
① 高频短连接——每次连接建立、断开,都伴随事务的开启与提交。如果连接没有复用,每秒数千次短连接,就等于每秒数千个 XID 被默默消耗。
② 逐行处理,每行独立事务——循环里对每一行都 BEGIN 一次、COMMIT 一次。1000 万行数据,就是 1000 万个 XID,再大的 XID 空间也经不起这么花。
③ 消息队列逐条确认——每条消息消费完都用独立事务做 ACK。高频消息流下,XID 的消耗速度远超清理速度。
④ ORM 默认自动提交——很多 ORM 默认开启 autocommit,每句 SQL 自动开一个事务。一次看似普通的批量更新,可能悄悄消耗成千上万个 XID。
⑤ 监控心跳频繁开启事务——探针每次心跳执行一条 SELECT 并提交,看起来无害,但高频心跳在持续消耗 XID。
⑥ 大表 + 高并发小事务——大表本身冻结就慢,再叠加高并发小事务,XID「进」的速度远快于「出」,雪上加霜。
怎么修?
立即止损(已经报 54000 错误)
# 单用户模式执行强制冻结
uxdb --single -D /opt/data template1
# 在单用户提示符下执行
VACUUM FREEZE;注意:单用户模式会停服,需要维护窗口。
参数修正
-- 核心修复:恢复 autovacuum 启动频率
ALTER SYSTEM SET autovacuum_naptime = '1min'; -- 从 23h 恢复为 1min
-- 加速冻结,避免再次来不及
ALTER SYSTEM SET vacuum_cost_limit = 2000; -- 默认 200,太保守
ALTER SYSTEM SET autovacuum_max_workers = 8; -- 更多并行 worker
-- 提前触发,留足时间窗口
ALTER SYSTEM SET autovacuum_freeze_max_age = '100000000'; -- 1亿(默认 2亿)
ALTER SYSTEM SET vacuum_freeze_table_age = '80000000'; -- 8000万
-- 大表单独加速
ALTER TABLE huge_table SET (autovacuum_vacuum_cost_limit = 10000);
-- 重载配置
SELECT ux_reload_conf();三个核心参数,一次讲透
先看一张总览图,XID age走到哪个位置、触发什么动作,一目了然:

autovacuum_naptime
autovacuum_freeze_max_age
vacuum_freeze_table_age
写在最后
autovacuum_naptime = 23h 等于关闭了 autovacuum。大量小事务疯狂消耗 XID,清理进程却 23 小时才动一次——崩溃不是意外,是参数埋下的定时炸弹。调参 + 合并事务,双管齐下才能根治。