DELETE后表膨胀主因是autovacuum未及时清理死元组,源于阈值过宽或长事务阻塞;需调低autovacuum_vacuum_threshold和scale_factor,并终止阻塞的长事务。

DELETE 后表膨胀严重,不是 DELETE 本身的问题,而是 autovacuum 没来得及清理死元组 —— 大多数情况是默认阈值太宽松,或长事务阻塞了清理。
autovacuum 为什么没及时清理死元组
PostgreSQL 的 DELETE(以及 UPDATE)不会物理删除数据,只打 xmax 标记;这些被标记的“死元组”必须由 VACUUM 扫描并回收。autovacuum 是否触发、何时触发,取决于两个关键条件是否同时满足:
-
pg_class.reltuples(估算行数) ×autovacuum_vacuum_scale_factor+autovacuum_vacuum_threshold≤ 当前死元组数量 - 该表自上次 vacuum 后,有足够多的事务修改(通过
pg_stat_all_tables.n_dead_tup监控)
默认 autovacuum_vacuum_scale_factor = 0.2,意味着:一张 1000 万行的表,要等产生 200 万 + 50 = 2000050 个死元组才触发 vacuum —— 这在高频 DELETE 场景下可能要等几小时甚至更久。而只要有一个长事务(state = 'active' 且 backend_start 很早),就会让 vacuum 跳过清理它可见范围内的死元组。
怎么快速定位正在阻塞 autovacuum 的长事务
执行以下查询,重点看 xmin 年龄极大、且状态为 active 的连接:
SELECT pid, usename, datname, backend_start, state,
age(xmin) AS xmin_age,
query
FROM pg_stat_activity
WHERE state = 'active' AND age(xmin) > 100000000
ORDER BY xmin_age DESC LIMIT 5;
如果发现某条记录 xmin_age 超过千万(约对应几天),且 query 是一个未提交的事务(比如开了 BEGIN 但没 COMMIT/ROLLBACK),它就是膨胀的直接推手。这类事务必须人工干预 —— 联系业务确认能否中断,或用 pg_terminate_backend(pid) 强制结束。
调低 autovacuum 触发阈值才管用
对高频写入/删除的关键表,不能依赖全局默认值。必须显式降低触发门槛,否则 vacuum 总是“慢半拍”:
- 把
autovacuum_vacuum_threshold从默认50改为20,让小量死元组就能触发 - 把
autovacuum_vacuum_scale_factor从0.2降到0.05(大型表可设0.02),避免大表“等不起” - 对单表生效:用
ALTER TABLE orders SET (autovacuum_vacuum_threshold = 20); - 别碰
autovacuum_analyze_*参数 —— 统计信息更新通常滞后一点影响不大
改完后,用 SELECT n_dead_tup, last_autovacuum FROM pg_stat_all_tables WHERE relname = 'orders'; 观察是否开始更频繁地自动运行。
VACUUM FULL 不是解药,反而可能是新问题的起点
VACUUM FULL 确实能彻底收缩文件、返还空间给操作系统,但它会申请表级排他锁,阻塞所有读写 —— 在生产环境等于停服。而且它不解决根本问题:只要死元组还在持续生成,几小时后又会膨胀。
真正要盯住的是:n_dead_tup 是否稳定在低位(比如 last_autovacuum 时间是否持续更新、是否有事务长期持有 xmin。这三个指标比磁盘空间大小更能说明 autovacuum 是否真正起效。










