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%。根据粒度不同,分为以下三种常用公式:

  1. 全局缓存命中率(基于 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');
  2. 表级缓存命中率(基于 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;
  3. 增量命中率(推荐用于性能测试)
    -- 取两次快照的差值,计算当前负载下的命中率
    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 缓存命中率与性能的关系

缓存命中率与性能影响关系图 高命中率 > 99% 中等命中率 95% – 99% 低命中率 < 95% ✅ 理想状态 几乎无磁盘 I/O shared_buffers 配置合理 ⚠️ 可接受 偶发磁盘读取 建议检查索引使用 ❌ 危险区域 大量磁盘 I/O 需优化 shared_buffers 或索引 性能优化建议 命中率 < 95% → 增大 shared_buffers / 检查索引选择性 / 考虑 pg_prewarm

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% 的表应引起注意。