Skip to content

Vacuum 与维护

死元组与表膨胀、VACUUM 原理、autovacuum、冻结与事务 ID 回卷、bloat 治理。

Updated View as Markdown
For humans

Vacuum 与维护

Vacuum 是 PostgreSQL 独有的核心运维考点,也是 PG 使用者最常见的生产事故源头。它存在的根本原因:MVCC 的旧版本留在堆里,必须有人清理。没有 undo log 的代价,就是 VACUUM。

死元组与表膨胀

UPDATE 和 DELETE 产生死元组(旧版本),它们仍占据页面空间:

  • 死元组不被当前任何快照可见后,才有资格被清理
  • 清理边界由最老的活动快照决定:长事务、空闲事务(idle in transaction)、复制槽会拖住清理,导致表持续膨胀
  • 表膨胀的直接后果:扫描变慢、缓存命中率下降、磁盘占用上升

生产现象:“没有锁等待但表越来越大”,就是死元组清理被阻塞。

VACUUM 做什么

VACUUM 的四项工作

普通 VACUUM 四件事:回收死元组空间(页内复用,不还给操作系统)、更新可见性映射(VM)、更新统计信息、推进冻结。VACUUM FULL 才重写表归还空间给文件系统,但要排他锁,生产慎用。

autovacuum

默认开启的自动清理:按阈值(autovacuum_vacuum_threshold 加比例)触发,后台进程自动运行。生产常见问题:

  • autovacuum 跟不上高 churn 表的死元组产生速度 → 手动调表级参数
  • 长事务阻塞 autovacuum → 查 pg_stat_activity 找 backend_xmin 持有者
  • 高更新率的表可单独配置更激进的 autovacuum 参数

冻结与事务 ID 回卷

PG 的事务 ID 是 32 位(约 42 亿次分配回卷一次)。元组的 xmin 如果保留太久,回卷后会从“过去”变成“未来”,导致可见性判断错误。解法是冻结:VACUUM 把老元组标记为 frozen(比所有普通事务都旧),并推进表的 relfrozenxid。

危险场景:事务 ID 回卷。当表的年龄(age)接近 20 亿,autovacuum 会强制执行防回卷清理;如果被阻塞(长事务、坏块、autovacuum 被禁用),系统接近回卷点时会拒绝分配新事务 ID,进入保护状态,整库不可写。这是 PG 最严重的事故之一。

预防:监控 age() 和数据库年龄,保持 autovacuum 健康,杀掉空闲事务(idle in transaction 是头号阻塞源),设置 idle_in_transaction_session_timeout

bloat 治理

  • 常规:依赖 autovacuum 维持稳态,页内复用即可
  • 严重膨胀:VACUUM FULL(停机窗口)或 pg_repack(在线重建表)
  • 预防:控制长事务、监控膨胀率(pg_stat_user_tables 的 dead tuple 和 n_live_tup)

面试追问

  1. 为什么 PG 需要 VACUUM? MVCC 旧版本留在堆里,没有 undo log。必须有人标记空间可复用
  2. VACUUM 和 VACUUM FULL 的区别? 普通 VACUUM 页内复用空间不还给系统;FULL 重写表归还空间但要排他锁
  3. 表膨胀的头号原因? 长事务和 idle in transaction 拖住清理边界,死元组无法回收。先查 backend_xmin 持有者
  4. 事务 ID 回卷是什么? 32 位 XID 回卷后可见性判断失效。冻结老元组推进 relfrozenxid,防回卷清理被阻塞会整库不可写
  5. 怎么监控膨胀? pg_stat_user_tables 看死元组和活元组比例,pg_stat_activity 找阻塞清理的事务
Navigation

Type to search…

↑↓ navigate↵ selectEsc close