死元组过多导致UPDATE变慢,需先用SQL查死元组占比(>10%才干预),再判断是否为autovacuum配置不当或长事务阻塞;普通VACUUM可在线清理复用空间,VACUUM FULL仅限收缩物理文件且需锁表,99%场景无需使用。

死元组太多导致UPDATE变慢,先确认是不是真问题
别急着跑 VACUUM,先查表里到底积了多少死元组。很多情况下 UPDATE 慢不是因为死元组多,而是索引缺失、统计信息过期或锁等待。用这条 SQL 快速定位:
SELECT relname, n_dead_tup, n_live_tup,
ROUND(100.0 * n_dead_tup / NULLIF(n_dead_tup + n_live_tup, 0), 2) AS dead_pct
FROM pg_stat_all_tables
WHERE relname = 'your_table_name';
-
n_dead_tup> 10% 的n_live_tup才值得干预;低于 5% 基本不用管 - 如果
n_dead_tup突然归零,说明 autovacuum 已经跑过,数据可能已被物理回收,恢复无望 - 注意看
last_autovacuum时间——如果它几小时没动过,说明 autovacuum 被阻塞或配置太保守
用 VACUUM FULL 还是普通 VACUUM?看场景选
VACUUM FULL 不是“更彻底的清理”,它是重建整张表,会加排他锁、吃光磁盘空间、且无法中断。99% 的日常性能修复根本不需要它。
- 普通
VACUUM:只清理死元组、释放空间供后续 INSERT/UPDATE 复用,不锁表(只在页级加轻量锁),适合线上表。执行:VACUUM (VERBOSE, ANALYZE) your_table_name; -
VACUUM FULL:仅在你明确要收缩物理文件大小(比如删了 80% 数据后想把表文件变小),且能接受数分钟到数小时锁表、额外磁盘空间(需等同于原表大小)时才用 - 千万别在高峰期跑
VACUUM FULL;也别对大表盲目加FREEZE——autovacuum 本身就会做冻结,手动触发反而干扰 XID 冻结节奏
autovacuum 配置不当才是根因,别总手动救火
频繁手动 VACUUM 说明 autovacuum 没起作用,得调参数,而不是靠人盯。
- 检查是否启用:
SHOW autovacuum;—— 必须是on,否则所有自动清理都失效 - 关键阈值别用默认值:对写入频繁的表,把
autovacuum_vacuum_scale_factor从默认 0.2 降到 0.05,避免等死元组堆到 20% 才动手 - 别只调 scale_factor:配合
autovacuum_vacuum_threshold(如设为 5000),确保小表也能及时触发清理 - 监控是否被阻塞:查
pg_stat_progress_vacuum视图,如果phase = 'vacuuming heap'卡住很久,可能是长事务或锁冲突
误删后想找回数据?死元组还在才能救
死元组不是“垃圾”,而是 MVCC 下的临时快照副本。只要它没被 autovacuum 清理掉,就能通过时间点或事务 ID 回溯读取。
- 立刻停写该表,防止新事务推进
vacuum_defer_cleanup_age或触发自动清理 - 用
pg_waldump解析 WAL 定位误删事务号(tx字段),再结合pg_resetwal回退事务可见性——但这需要离线操作且风险极高,生产环境慎用 - 真正靠谱的方案是提前设好
vacuum_defer_cleanup_age = 100000(延迟 10 万事务再清理死元组),给误操作留出抢救窗口 - 记住:TRUNCATE 和 DROP TABLE 后的死元组已不复存在,WAL 里也没记录行级内容,这种删法基本不可逆
实际清理效果差异极大——同样一张千万行表,VACUUM 后 UPDATE 延迟从 800ms 降到 40ms 是常态,但如果你发现清理完没改善,大概率是索引膨胀或查询计划没更新,得接着查 pg_stat_all_indexes 和 EXPLAIN ANALYZE。










