PostgreSQLNotes

第 17 章:VACUUM

zjc 于 2026-01-17 发布

这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 VACUUM 负责回收死元组空间、冻结事务 ID、维护可见性映射和更新统计相关信息。它是 PostgreSQL 长期健康运行的关键机制。

17.1 为什么需要 VACUUM

UPDATE / DELETE
  -> 产生死元组
  -> 旧快照不再需要后
  -> VACUUM 标记空间可复用

VACUUM 做的事情:

  1. 回收死元组;
  2. 截断尾部空页;
  3. 冻结旧事务;
  4. 更新可见性映射;
  5. 配合 Index Only Scan;
  6. ANALYZE 更新统计。

普通 VACUUM 不归还空间给操作系统,只让页内空间可复用。

17.2 autovacuum

查看:

SHOW autovacuum;
SHOW autovacuum_vacuum_threshold;
SHOW autovacuum_vacuum_scale_factor;

触发估算:

dead tuples > threshold + scale_factor * live tuples

表级参数:

ALTER TABLE orders SET (
    autovacuum_vacuum_scale_factor = 0.02,
    autovacuum_vacuum_threshold = 1000
);

大表建议调小 scale factor,否则要积累大量死元组才触发。

17.3 手动 VACUUM

VACUUM orders;
VACUUM ANALYZE orders;
VACUUM FULL orders;
命令 作用
VACUUM ShareUpdateExclusive 清理死元组
VACUUM FULL AccessExclusive 重写表并收缩
ANALYZE ShareUpdateExclusive 更新统计

生产大表通常避免 VACUUM FULL,膨胀严重时使用在线重建方案。

17.4 可见性映射

可见性映射记录数据页是否所有事务可见。页全可见时:

  1. Index Only Scan 更高效;
  2. VACUUM 可跳过部分页;
  3. 查询减少回表判断。

频繁更新的表会导致可见性映射失效,索引只扫性能下降。

17.5 冻结与防回卷

SHOW vacuum_freeze_min_age;
SHOW vacuum_freeze_table_age;
SHOW autovacuum_freeze_max_age;

当表年龄接近上限,PostgreSQL 会执行激进清理,甚至拒绝写入以防止事务 ID 回卷。

查看年龄:

SELECT
    c.oid::regclass AS table_name,
    age(c.relfrozenxid) AS xid_age
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')
ORDER BY xid_age DESC;

17.6 阻塞 VACUUM 的因素

  1. 长事务;
  2. 空闲事务;
  3. 慢查询;
  4. 逻辑复制槽;
  5. 备库 hot_standby_feedback;
  6. prepared transaction;
  7. 备份工具保留快照。

查看长事务:

SELECT pid, state, xact_start, query
FROM pg_stat_activity
WHERE xact_start IS NOT NULL
ORDER BY xact_start
LIMIT 10;

17.7 表膨胀判断

SELECT
    relname,
    n_live_tup,
    n_dead_tup,
    round(n_dead_tup::numeric / greatest(n_live_tup, 1), 4) AS dead_ratio
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC;

统计值只是估算,精确判断需要扩展或物理页分析。持续增长、扫描变慢和索引膨胀是重要信号。

17.8 VACUUM 调优

SHOW maintenance_work_mem;
SHOW autovacuum_max_workers;
SHOW autovacuum_vacuum_cost_limit;

建议:

  1. 大表单独设置阈值;
  2. 增加维护内存;
  3. 控制 worker 数量与 IO 限速;
  4. 低峰执行大批量导入后的清理;
  5. 监控 last_autovacuum;
  6. 治理长事务。

17.9 索引膨胀

查看:

SELECT
    relname,
    indexrelname,
    idx_scan,
    pg_size_pretty(pg_relation_size(indexrelid)) AS size
FROM pg_stat_user_indexes
ORDER BY pg_relation_size(indexrelid) DESC;

处理:

REINDEX INDEX index_name;
REINDEX TABLE CONCURRENTLY table_name;

本章小结

VACUUM 是 MVCC 的清道夫。生产上要为高频更新表调优 autovacuum,持续治理长事务和复制槽,监控死元组、表年龄和 last_autovacuum。膨胀治理的重点是预防,而不是频繁 VACUUM FULL。

思考题

  1. VACUUM 和 ANALYZE 的区别是什么?
  2. 为什么 VACUUM FULL 生产慎用?
  3. 长事务如何影响 VACUUM?
  4. 表年龄过高意味着什么?
  5. 大表 autovacuum 阈值如何调整?