2. 缓存命中率:pg_stat_bgwriter 与 pg_statio_user_tables 视图解读
缓存命中率是衡量 PostgreSQL 共享缓冲区(shared_buffers)效率的核心指标。高命中率意味着大多数数据请求直接从内存返回,避免昂贵的磁盘 I/O。本章将深入解读两个关键系统视图——pg_stat_bgwriter 和 pg_statio_user_tables,并给出精确的缓存命中率计算公式。
2.1 核心视图对比
| 视图名称 | 作用域 | 关键字段 | 用途 |
|---|---|---|---|
pg_stat_bgwriter |
整个数据库集群 | buffers_alloc, buffers_backend, buffers_checkpoint, maxwritten_clean |
评估后台写入器效率、检查点冲刷频率、后端直接写入压力 |
pg_statio_user_tables |
用户表级别 | heap_blks_read, heap_blks_hit, idx_blks_read, idx_blks_hit |
计算表和索引的缓存命中率,定位低命中率的对象 |
实践提示:
pg_statio_user_tables 提供的是 累计值(从实例启动或统计信息重置开始),计算命中率时通常取两个时间点的差值(增量),以反映当前负载特征。
2.2 缓存命中率计算公式
缓存命中率 = 命中次数 / (命中次数 + 读取次数) × 100%。根据粒度不同,分为以下三种常用公式:
- 全局缓存命中率(基于
pg_stat_bgwriter)
-- 全局命中率 ≈ 1 - (buffers_backend / (buffers_alloc + buffers_backend)) -- 简化版:使用 pg_stat_database 的 blks_hit 与 blks_read SELECT ROUND( 100.0 * SUM(blks_hit) / NULLIF(SUM(blks_hit + blks_read), 0), 2 ) AS global_cache_hit_ratio FROM pg_stat_database WHERE datname NOT IN ('template0', 'template1', 'postgres'); - 表级缓存命中率(基于
pg_statio_user_tables)
SELECT schemaname, relname, heap_blks_hit, heap_blks_read, ROUND( 100.0 * heap_blks_hit / NULLIF(heap_blks_hit + heap_blks_read, 0), 2 ) AS heap_hit_ratio, idx_blks_hit, idx_blks_read, ROUND( 100.0 * idx_blks_hit / NULLIF(idx_blks_hit + idx_blks_read, 0), 2 ) AS idx_hit_ratio FROM pg_statio_user_tables WHERE (heap_blks_hit + heap_blks_read) > 1000 -- 过滤小表 ORDER BY heap_hit_ratio ASC LIMIT 10; - 增量命中率(推荐用于性能测试)
-- 取两次快照的差值,计算当前负载下的命中率 WITH snap1 AS ( SELECT SUM(heap_blks_hit) AS hit1, SUM(heap_blks_read) AS read1 FROM pg_statio_user_tables ), snap2 AS ( SELECT SUM(heap_blks_hit) AS hit2, SUM(heap_blks_read) AS read2 FROM pg_statio_user_tables ) SELECT ROUND( 100.0 * (hit2 - hit1) / NULLIF((hit2 - hit1) + (read2 - read1), 0), 2 ) AS incremental_hit_ratio FROM snap1, snap2;
注意: 如果
heap_blks_hit + heap_blks_read 接近 0(例如新建的空表),命中率计算会除零,务必使用 NULLIF 保护。另外,pg_stat_bgwriter 中的 buffers_backend 代表后端进程直接读取磁盘的次数,该值越高说明缓存未命中越严重。
2.3 缓存命中率与性能的关系
2.4 解读 pg_stat_bgwriter 关键指标
| 字段 | 含义 | 正常范围 | 异常信号 |
|---|---|---|---|
buffers_alloc |
分配的缓冲区总数 | 持续增长 | 无增长可能表示无活动 |
buffers_backend |
后端进程直接读取磁盘的缓冲区数 | 占 buffers_alloc 比例 < 5% |
比例过高 → 缓存未命中严重 |
buffers_checkpoint |
检查点写入的缓冲区数 | 与 buffers_alloc 比例合理 |
过高 → 检查点过于频繁或 dirty 页过多 |
maxwritten_clean |
后台写入器因写入过多而停止的次数 | 接近 0 | 频繁触发 → 需调整 bgwriter 参数 |
诊断示例: 如果
buffers_backend / buffers_alloc > 0.1,说明超过 10% 的数据请求穿透了共享缓冲区,直接落到磁盘。此时应优先检查 pg_statio_user_tables 中 heap_blks_read 最高的表,分析其查询计划是否缺少索引。
2.5 解读 pg_statio_user_tables 关键指标
heap_blks_hit:从共享缓冲区中命中的堆块数。heap_blks_read:从磁盘读取的堆块数(包括操作系统缓存命中,但 PostgreSQL 视角仍算 read)。idx_blks_hit:从共享缓冲区中命中的索引块数。idx_blks_read:从磁盘读取的索引块数。
典型低命中率场景:
- 全表扫描频繁的表:
heap_blks_read远高于heap_blks_hit。 - 索引未被使用:
idx_blks_hit接近 0,但表有索引。 - 索引膨胀:索引块数远大于实际数据块数,导致缓存效率下降。
快速定位问题表: 使用上面给出的表级命中率查询,按
heap_hit_ratio 升序排列,前 5 个表就是需要优先优化的对象。通常命中率低于 90% 的表应引起注意。