← 中文资源

简体中文版本

检测并收缩膨胀的 Postgres 表

用 pgstattuple 估算死元组膨胀,用 pg_repack 无排他锁回收空间,并验证表体积真实下降。

阅读英文原文

目标

频繁 UPDATE/DELETE 留下的死元组会被 autovacuum 标记为可复用,但空间从不归还操作系统。表持续膨胀、每次顺序扫描都在读千兆字节的“尸体”、磁盘预测飘红。pgstattuple 精确量化浪费,pg_repack 以短暂锁在线回收,再配合按表调优的 autovacuum 让尸体密度从此不再累积。

步骤要点

  1. 安装 pgstattuple 扩展并对目标表执行精确统计,确认 dead_tuple_percent(如 62%)与平坦的行数形成对照。
  2. 查 pg_stat_user_tables 的 n_dead_tup 与 last_autovacuum,解释为什么空间从未被自动回收。
  3. 用 pg_repack -t orders -j 2 在线重建:读写不中断,仅在切换瞬间持短暂锁。
  4. 复核 pg_total_relation_size 与 pgstattuple:表从 480GB 缩到 150GB,死元组率降到 1% 以下。
  5. 给该表设 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)