SQL 如何用 pgstattuple 排查表/索引膨胀问题

5次阅读

pgstattuple 返回 pagetotal、pageused、pagefree、deadtuplecount 和 deadtuplelen 五个关键膨胀指标,其中 pagefree 占比和 deadtuplecount 增长趋势直接反映表膨胀程度。

SQL 如何用 pgstattuple 排查表/索引膨胀问题

pgstattuple 函数返回哪些关键膨胀指标

pgstattuplepostgresql 提供的系统函数,用于返回表或索引的物理存储细节。它不直接说“膨胀了”,而是给出几个核心数值:占用页数(pagetotal)、实际存数据的页数(pageused)、空闲页数(pagefree)、死亡元组数(deadtuplecount)和死亡元组所占字节deadtuplelen)。真正反映膨胀程度的是 pagefree 占比和 deadtuplecount 是否持续增长——比如某表 pagetotal=10000pageused=3000,说明近 70% 页面是空的,大概率需要 VACUUMVACUUM FULL

如何对单个表运行 pgstattuple 并解读结果

确保已加载 pgstattuple 扩展(CREATE EXTENSION if NOT EXISTS pgstattuple;),然后执行:

select * FROM pgstattuple('public.orders');

重点关注以下几项:

  • deadtuplecount > 0 且近期写入频繁 → 可能存在未清理的死亡元组
  • pagefree > pagetotal * 0.2(即空闲空间超 20%)→ 物理膨胀较明显
  • avg_dead_tuple_len > 0 且值较大 → 死亡元组体积不小,单纯 VACUUM 可能无法回收全部空间(需 VACUUM FULLCLUSTER
  • free_percent 字段(部分版本提供)若 > 25%,基本可判定需干预

索引膨胀怎么查?用 pgstatindex 替代 pgstattuple

pgstattuple 不适用于索引;查索引得用 pgstatindex。它返回类似结构,但关注点不同:

  • index_size 是索引总大小,used_size 是实际被键值使用的字节
  • leaf_fragmentation > 15% 表示叶页碎片严重,查询性能可能下降
  • avg_leaf_density 过低(如
  • 执行:
    SELECT * FROM pgstatindex('orders_user_id_idx');

注意:B-tree 索引膨胀后,VACUUM 对其无效,必须用 REINDEXCREATE INDEX CONCURRENTLY 替换。

为什么 pgstattuple 结果有时不准或报错

常见原因包括:

  • 用户无目标表的 SELECT 权限 → 报错 permission denied for table xxx,需先授予权限或切到超级用户
  • 表正被长时间持有 access EXCLUSIVE 锁(如大事务、ALTER TABLE)→ pgstattuple 会卡住或超时,建议避开业务高峰执行
  • 在只读备库上执行 → 返回错误 cannot execute pgstattuple in a read-only transaction,必须在主库查
  • 对分区表主表调用 → 返回的是主表自身的元数据(不含子表),要查每个子表单独执行,例如 pgstattuple('orders_2024_q1')

真实生产中,膨胀往往不是单次查询能定论的,得结合 pg_stat_all_tablesn_dead_tup 和历史趋势一起看——比如连续三天 n_dead_tup 增长 50 万,但 last_vacuum 是一周前,那问题就比较明确了。

text=ZqhQzanResources