必须手动执行VACUUM和ANALYZE的情况包括:大批量DELETE/UPDATE后查询变慢、百万级数据导入后索引未生效、n_dead_tup远高于n_tup_ins且last_autovacuum久远、autovacuum被禁用未恢复。

什么时候该手动执行 VACUUM 和 ANALYZE
PostgreSQL 的 autovacuum 通常能应付大多数场景,但以下情况必须手动干预:
- 执行大批量
DELETE或UPDATE后,查询明显变慢(说明死元组堆积、页面膨胀) - 刚导入百万级以上数据,但后续查询没走索引(
ANALYZE没触发或滞后) -
pg_stat_all_tables中n_dead_tup远高于n_tup_ins,且last_autovacuum时间久远 - 手动禁用了 autovacuum(比如临时调试),又忘了补上
注意:VACUUM 不锁表(只加 SHARE UPDATE EXCLUSIVE 锁),但会读全表;VACUUM FULL 会锁表、重建表、阻塞所有写入——除非磁盘空间告急且无法扩容,否则别用。
VACUUM 和 ANALYZE 能分开执行吗
可以,而且经常需要分开:
-
VACUUM主要回收死元组空间、更新visibility map,不更新统计信息 -
ANALYZE只扫描样本页、生成列值分布和相关性数据,不清理任何数据 - 常见组合是
VACUUM ANALYZE table_name,但若表极大(>10GB),建议分步:先VACUUM(释放空间、减少后续ANALYZE扫描量),再ANALYZE(确保统计准确) - 如果只改了某几列的分布(比如批量更新了
status字段),可用ANALYZE table_name (status)针对单列重采样,更快更轻量
示例:
VACUUM orders;
ANALYZE orders (created_at, status);
如何验证统计是否已更新
别信“执行完就生效”,得查系统视图确认:
- 检查
last_analyze和last_autoanalyze:SELECT schemaname, tablename, last_analyze, last_autoanalyze FROM pg_stat_all_tables WHERE tablename = 'orders';
- 查看实际统计内容是否合理:
SELECT tablename, attname, n_distinct, most_common_vals FROM pg_stats WHERE tablename = 'orders' AND attname = 'status';
如果most_common_vals还是空或明显过时(比如status已全变成'shipped',但统计里还显示'pending'占多数),说明ANALYZE没生效或采样不足 - 注意:统计变更后,查询计划不一定立刻刷新——旧的执行计划可能被缓存,可执行
DISCARD PLANS;强制重编译(仅会话级)
容易被忽略的细节和坑
-
ANALYZE默认采样率由default_statistics_target控制(默认 100),对倾斜数据(如 99% 是'active',1% 是其他值)可能漏掉长尾值;必要时对关键列调高:ALTER TABLE users ALTER COLUMN role SET STATISTICS 500;
-
VACUUM不会降低relfrozenxid,长期不 vacuum 可能触发事务 ID 回卷(xid wraparound),这是严重故障,不是性能问题 - 在主从架构中,
VACUUM和ANALYZE只能在主库运行,从库只读,统计信息通过 WAL 复制同步——但同步有延迟,从库的pg_stats可能短暂滞后 - 使用
pg_repack或CLUSTER时,内部会触发ANALYZE,但不保证及时,建议之后手动补一次
真正卡住人的,往往不是不会敲命令,而是没盯住 last_analyze 时间戳,也没验证 pg_stats 里的实际分布。










