目标
频繁 UPDATE/DELETE 留下的死元组会被 autovacuum 标记为可复用,但空间从不归还操作系统。表持续膨胀、每次顺序扫描都在读千兆字节的“尸体”、磁盘预测飘红。pgstattuple 精确量化浪费,pg_repack 以短暂锁在线回收,再配合按表调优的 autovacuum 让尸体密度从此不再累积。
步骤要点
- 安装 pgstattuple 扩展并对目标表执行精确统计,确认 dead_tuple_percent(如 62%)与平坦的行数形成对照。
- 查 pg_stat_user_tables 的 n_dead_tup 与 last_autovacuum,解释为什么空间从未被自动回收。
- 用 pg_repack -t orders -j 2 在线重建:读写不中断,仅在切换瞬间持短暂锁。
- 复核 pg_total_relation_size 与 pgstattuple:表从 480GB 缩到 150GB,死元组率降到 1% 以下。
- 给该表设 autovacuum_vacuum_scale_factor=0.02 与更低的 cost_delay,让清理频率跟上更新节奏。
排错提示
pg_repack 起步拿不到锁通常是长事务占着,结束或等它们退出并把 repack 排到低峰;磁盘放不下重建副本就先清 WAL 与旧备份,或按分区逐个 repack;几周内再次膨胀说明更新频率超过了默认 autovacuum,必须下调 scale factor 与 cost delay 并用 pg_stat_user_tables 验证。另外 VACUUM FULL 会持排他锁阻塞一切,规模化场景应弃用。
适用边界
pgstattuple 精确模式要全表扫描、代价高,大表在营业时段应使用近似模式;repack 需要临时的双倍存储与超级用户权限,部分托管平台会限制或代为调度。
代码示例
# Quantify
SELECT * FROM pgstattuple('orders');
# Reclaim online (reads and writes continue)
pg_repack -d shop -t orders -j 2
# Verify
SELECT pg_size_pretty(pg_total_relation_size('orders'));
# pg_total_relation_size
# ------------------------
# 4121 MB (was 18.9 GB)