直接查pg_stat_all_tables,重点看dead_tup_ratio字段,超过50%即表明表严重膨胀需干预;执行SQL筛选dead_tup_ratio≥50的表并按该比率降序排列,同时使用NULLIF避免除零错误,且用pg_total_relation_size反映含索引和TOAST的真实磁盘压力。

怎么快速定位膨胀率超过50%的表
直接查 pg_stat_all_tables,重点看 dead_tup_ratio——它表示死元组占总元组数的百分比。超过50%就说明该表已经严重膨胀,必须干预。
执行这条 SQL(替换 mydb 为你实际数据库名):
SELECT schemaname||'.'||relname AS table_name,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||relname)) AS total_size,
n_dead_tup, n_live_tup,
ROUND(n_dead_tup * 100.0 / NULLIF(n_live_tup + n_dead_tup, 0), 1) AS dead_tup_ratio
FROM pg_stat_all_tables
WHERE (n_live_tup + n_dead_tup) > 0
AND ROUND(n_dead_tup * 100.0 / NULLIF(n_live_tup + n_dead_tup, 0), 1) >= 50
ORDER BY dead_tup_ratio DESC;
- 注意
NULLIF防止除零错误,这是线上脚本里容易漏掉的细节 -
pg_total_relation_size()包含索引和 TOAST,比pg_relation_size()更反映真实磁盘压力 - 如果结果为空但磁盘仍在涨,可能是索引膨胀,得单独查
pg_indexes
VACUUM FULL 执行前必须确认的三件事
VACUUM FULL 不是“一键清理”,它会重建整个表文件,过程中持有排他锁(ACCESS EXCLUSIVE),所有读写都会被阻塞。执行前务必确认:
- 业务低峰期窗口是否足够长?一张 10GB 的表在机械盘上可能耗时 15 分钟以上
- 磁盘剩余空间是否 ≥ 当前表大小的 2 倍?因为它是先建新文件、再删旧文件
- 是否已关闭 autovacuum 对该表的干扰?临时执行:
ALTER TABLE mytable SET (autovacuum_enabled = false);
没确认这三项就跑 VACUUM FULL,极大概率导致服务中断或磁盘写满失败。
执行 VACUUM FULL 的正确姿势
不要在连接池或应用侧直接发命令,用独立 psql 会话,并加 VERBOSE 观察进度:
VACUUM FULL VERBOSE mytable;
- 输出中出现
progress: 10/100这类提示,说明进度报告已启用;若没有,检查log_min_messages是否设为INFO或更低 - 如果卡在某个阶段超 5 分钟,立刻查
pg_stat_activity看是否有长事务阻塞(xmin未推进) - 别用
VACUUM FULL ANALYZE一起跑——ANALYZE 可以单独跟在后面做,避免延长锁时间
为什么 VACUUM FULL 后空间没立刻变小
常见现象:执行完 VACUUM FULL,pg_total_relation_size() 确实下降了,但操作系统层面 du -sh 查数据目录,文件大小没变。这是因为:
- PostgreSQL 默认使用 fsync,但文件系统可能延迟释放块(尤其 XFS + nobarrier 场景)
- 旧文件删除依赖 WAL 归档清理和 checkpoint 完成,若
checkpoint_timeout设得过大(比如 30min),要等下一个 checkpoint 才真正落盘 - 更隐蔽的情况:表上有未提交的 long-running transaction,其 xmin 拖住了 vacuum full 的清理边界
此时别反复重跑,先查 pg_replication_slots 和 pg_stat_activity,确认没有滞留事务或复制槽卡住 xmin。










