pgstattuple 是 PostgreSQL 的系统扩展,通过直接读取页级结构精确统计表的物理存储细节,不依赖事务可见性判断,因此比 VACUUM VERBOSE 的启发式估算更可靠。

pgstattuple 是什么,为什么它比 VACUUM 的输出更可靠
pgstattuple 是 PostgreSQL 提供的一个系统扩展,用于精确统计表或索引的物理存储细节,比如实际占用页数、空闲空间占比、死亡元组数量等。它不依赖事务可见性判断,直接读取页级结构,所以比 VACUUM VERBOSE 输出的“bloat estimate”更准确——后者只是启发式估算,容易在大表或高并发写入场景下严重失真。
启用前需确保已安装:
CREATE EXTENSION pgstattuple;注意:必须由超级用户或具有
CREATE 权限的用户执行,且只对当前数据库生效。
查单个表的膨胀率:用 pgstattuple 还是 pgstatindex
查表用 pgstattuple,查 B-tree 索引用 pgstatindex——两者接口相似但底层逻辑不同。混淆会导致结果无意义,比如对索引调用 pgstattuple 会报错或返回乱码数据。
检查某张表的实际膨胀情况:
SELECT table_len, tuple_count, tuple_len, dead_tuple_count, dead_tuple_len, free_space, free_percent FROM pgstattuple('public.my_table');
-
free_percent> 20% 通常值得警惕;> 30% 建议立即干预 -
dead_tuple_count持续增长但VACUUM频次不足,说明 autovacuum 可能被抑制(如autovacuum_vacuum_scale_factor设得过大) - 注意
table_len是以字节为单位,不是页数;除以 8192 才得实际页数
批量识别膨胀严重的表和索引
生产环境不能手动一张张查,要用脚本聚合。以下查询找出 free_percent 超过 25% 的常规表(排除系统表和 TOAST 表):
SELECT
nspname AS schema,
relname AS table_name,
ROUND((s.free_percent)::numeric, 1) AS free_pct,
s.table_len,
s.dead_tuple_count
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
JOIN pgstattuple(c.oid) s ON true
WHERE c.relkind = 'r'
AND n.nspname NOT IN ('pg_catalog', 'information_schema')
AND c.relname !~ '^pg_toast'
AND s.free_percent > 25
ORDER BY s.free_percent DESC;
对应地,查 B-tree 索引膨胀:
SELECT nspname AS schema, i.indexrelname AS index_name, ROUND((s.bloat)::numeric, 1) AS bloat_pct, s.index_size FROM pg_index i JOIN pg_class c ON c.oid = i.indrelid JOIN pg_class ic ON ic.oid = i.indexrelid JOIN pg_namespace n ON n.oid = ic.relnamespace JOIN pgstatindex(ic.oid) s ON true WHERE ic.relkind = 'i' AND s.bloat > 25 ORDER BY s.bloat DESC;
-
pgstatindex返回的bloat字段是百分比估值,不是精确值,但它对索引有效;而pgstattuple对索引不适用 - 这两个查询本身不锁表,但频繁调用仍可能加重 I/O,建议在低峰期跑,或加
LIMIT 20快速定位头部问题对象
查到膨胀后,VACUUM 和 VACUUM FULL 怎么选
发现膨胀 ≠ 立刻 VACUUM FULL。VACUUM FULL 会锁表排他、重写整个表,对在线业务风险极高,仅适用于维护窗口内且无其他选择的场景。
日常应优先:
VACUUM (VERBOSE, ANALYZE) public.my_table;
- 加
VERBOSE可看到实际回收了多少页、多少元组,验证是否生效 - 如果
dead_tuple_count降了但free_percent几乎没变,说明空闲空间未被操作系统回收(常见于 ext4 文件系统 +fsync=off),此时需配合pg_repack或计划停机做VACUUM FULL - 对索引,
VACUUM无效,必须用REINDEX INDEX或REINDEX TABLE—— 但注意REINDEX会短暂锁索引写入
真正棘手的是那些持续高频更新的小表:它们死亡元组生成快、autovacuum 来不及清理,pgstattuple 显示 free_percent 波动剧烈。这种表要调低 autovacuum_vacuum_scale_factor(比如设成 0.01),并提高 autovacuum_vacuum_cost_limit,否则默认配置根本追不上。










