PostgreSQL 在 autovacuum 启用时,当死元组数 ≥ threshold + scale_factor × 总行数,或变更行数达 analyze 阈值,或事务 ID 接近 autovacuum_freeze_max_age 限制时,自动触发 VACUUM 或 ANALYZE;但 UNLOGGED 表、临时表及系统表(未显式启用)除外。

什么时候 PostgreSQL 会自动触发 VACUUM 和 ANALYZE
PostgreSQL 默认开启 autovacuum,只要表有足够多的死元组(dead tuples)或统计信息过期,就会自动调度 VACUUM 和 ANALYZE。但“足够多”不是固定行数,而是基于几个动态阈值计算出来的:
-
autovacuum_vacuum_threshold(默认 50)+autovacuum_vacuum_scale_factor(默认 0.2)→ 触发VACUUM的死元组下限 =threshold+scale_factor × 表总行数 -
autovacuum_analyze_threshold(默认 50)+autovacuum_analyze_scale_factor(默认 0.1)→ 触发ANALYZE的变更行数下限同理 - 实际是否运行还受
autovacuum_freeze_max_age(默认 2 亿事务)约束:哪怕没改数据,只要事务 ID 快耗尽,也会强制VACUUM
注意:autovacuum 不会为 UNLOGGED 表或临时表运行,也不处理系统表(除非显式配置 autovacuum_enabled = on)。
哪些场景必须手动执行 VACUUM / ANALYZE
自动机制有延迟、有保守性,以下情况无法等待:
- 刚批量
DELETE或UPDATE掉表 80% 以上数据,但表仍很大 → 死元组堆积严重,查询可能变慢,空间不释放 - 执行完大批次
INSERT后立刻做复杂查询 → 优化器还没拿到新数据分布统计,容易选错执行计划 -
pg_stat_all_tables.n_dead_tup持续高于pg_class.reltuples × 0.2,且last_autovacuum时间很久 → 自动进程被阻塞或配置过松 - 升级 PostgreSQL 小版本后首次启库 → 建议对所有用户表跑一次
VACUUM ANALYZE,避免旧统计残留影响
手动命令最常用组合是 VACUUM (VERBOSE, ANALYZE) table_name,加 VERBOSE 能看到实际清理了多少行、是否触发了冻结(freeze)。
VACUUM FULL 和普通 VACUUM 的关键区别
VACUUM FULL 不是“更彻底的 VACUUM”,而是完全不同的操作——它会重写整个表并重建索引,释放磁盘空间,但代价极高:
- 需要
ACCESS EXCLUSIVE锁,阻塞所有读写 - 全程占用双倍磁盘空间(新旧表同时存在)
- 不会跳过未修改的页,即使表只有几行变更也要全量重写
- 不推荐在业务高峰期执行;日常维护优先用普通
VACUUM+ 调整autovacuum_vacuum_cost_limit加速
真正需要 VACUUM FULL 的典型场景极少:比如某张表长期只删不插,pg_total_relation_size() 居高不下,且确认没有长事务或复制槽拖慢普通 VACUUM 进度。
ANALYZE 的采样逻辑和精度控制
ANALYZE 默认对每列按数据分布做采样,不是全表扫描。采样行数由 default_statistics_target(默认 100)控制,但实际采样量还取决于表大小和列基数:
- 小表(default_statistics_target × 列数 条统计样本
- 如果某列常用于
WHERE或JOIN条件但基数极高(如 UUID),默认采样可能不够准 → 可单独提高该列目标:ALTER TABLE t ALTER COLUMN c SET STATISTICS 500 - 执行
ANALYZE时加(SAMPLE_RATE 0.5)可强制指定采样率,但需谨慎:过低导致统计失真,过高增加 I/O 开销
一个容易被忽略的点:ANALYZE 不会更新 pg_class.reltuples(估算总行数),这个值只在 VACUUM 或 ANALYZE 带 VERBOSE 时顺带更新;若依赖它做容量预估,得确保定期触发含 VERBOSE 的分析。










