这是《PostgreSQL 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 VACUUM 负责回收死元组空间、冻结事务 ID、维护可见性映射和更新统计相关信息。它是 PostgreSQL 长期健康运行的关键机制。
17.1 为什么需要 VACUUM
UPDATE / DELETE
-> 产生死元组
-> 旧快照不再需要后
-> VACUUM 标记空间可复用
VACUUM 做的事情:
- 回收死元组;
- 截断尾部空页;
- 冻结旧事务;
- 更新可见性映射;
- 配合 Index Only Scan;
- 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 可见性映射
可见性映射记录数据页是否所有事务可见。页全可见时:
- Index Only Scan 更高效;
- VACUUM 可跳过部分页;
- 查询减少回表判断。
频繁更新的表会导致可见性映射失效,索引只扫性能下降。
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 的因素
- 长事务;
- 空闲事务;
- 慢查询;
- 逻辑复制槽;
- 备库 hot_standby_feedback;
- prepared transaction;
- 备份工具保留快照。
查看长事务:
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;
建议:
- 大表单独设置阈值;
- 增加维护内存;
- 控制 worker 数量与 IO 限速;
- 低峰执行大批量导入后的清理;
- 监控 last_autovacuum;
- 治理长事务。
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。
思考题
- VACUUM 和 ANALYZE 的区别是什么?
- 为什么 VACUUM FULL 生产慎用?
- 长事务如何影响 VACUUM?
- 表年龄过高意味着什么?
- 大表 autovacuum 阈值如何调整?