3. PostgreSQL关键运行指标与健康基线

生产环境的 PostgreSQL 是否“健康”,不能靠感觉判断,也不能只看 CPU 和内存。数据库内部有一组更能反映真实压力的指标:连接数、锁等待、复制延迟、WAL 生成速率、检查点频率、缓存命中率。它们分别对应资源占用、并发冲突、高可用同步、写入强度、后台刷盘节奏和内存效率。理解每个指标的正常范围与异常信号,是应急响应和恢复演练的第一道判断依据。

需要先明确一个前提:健康基线不是一组固定数字,而是“在业务正常时段采集到的稳定区间”。同一套参数,在 OLTP 交易库和报表分析库上的合理值可能相差数倍。因此本节给出的数值均为教学示例或经验参考,实际基线必须来自本系统在业务低峰与高峰时段的持续采样。

连接数:先看占用率,再看等待来源

连接数反映客户端会话对数据库的占用。核心不是绝对数量,而是相对 max_connections 的占用率,以及连接处于什么状态。通过以下查询可以快速掌握全局:

SELECT state, count(*)
FROM pg_stat_activity
GROUP BY state ORDER BY 2 DESC;

SELECT count(*) AS total,
       count(*) FILTER (WHERE state = 'active') AS active,
       count(*) FILTER (WHERE wait_event_type = 'Lock') AS lock_wait
FROM pg_stat_activity;

经验上,连接占用率长期低于 70% 属于安全区;70%~85% 需要关注;超过 85% 或出现 FATAL: sorry, too many clients already 即为异常信号。但更危险的是“连接不多却大量 active 且等待”。如果 active 数量持续接近连接上限,同时 wait_event_type 集中在 Lock、IO 或 LWLock,说明瓶颈不在连接数本身,而在锁竞争或 I/O。此时盲目调大 max_connections 只会加重内存与上下文切换负担,正确做法是定位阻塞源头。

锁等待:区分瞬时争用与长事务阻塞

锁等待是并发写入最典型的异常信号。正常业务下,短暂的行锁等待(毫秒级)不可避免;但若出现秒级以上的等待,或等待会话数量持续增长,就说明存在长事务或未提交事务持有锁。定位阻塞链的关键查询是:

SELECT w.pid AS waiting_pid,
       w.query AS waiting_query,
       b.pid AS blocking_pid,
       b.query AS blocking_query,
       now() - b.xact_start AS blocking_age
FROM pg_stat_activity w
JOIN pg_stat_activity b
  ON b.pid = ANY(pg_blocking_pids(w.pid))
WHERE w.wait_event_type = 'Lock';

判断标准:阻塞事务年龄超过业务最长事务时间(例如 30 秒)即应告警;若 blocking_age 达到分钟级,通常意味着应用侧存在忘记提交、批量更新未分批或 DDL 与 DML 冲突。处理时优先联系业务确认,必要时用 pg_cancel_backend 取消等待者,用 pg_terminate_backend 终止阻塞源,并记录现场用于复盘。

复制延迟:高可用场景的生命线

对于流复制或逻辑复制,延迟直接决定故障切换时可能丢失多少数据。主库查看 pg_stat_replication 的 write_lag、flush_lag、replay_lag;备库用 pg_last_wal_receive_lsn() 与 pg_last_wal_replay_lsn() 的差值估算积压字节数。教学示例中,同机房同步复制延迟通常应小于 1 秒,跨机房异步复制可放宽到数秒。若 replay_lag 持续增长且不回落,说明备库回放能力不足,常见原因是备库磁盘 I/O 饱和、max_worker_processes 或恢复参数不合理,或主库 WAL 生成速率超过备库消费速率。

WAL 生成速率与检查点频率:写入强度的两面

WAL 生成速率反映单位时间内产生的预写日志量,可用 pg_stat_wal 或两次采样 pg_current_wal_lsn() 的差值除以时间间隔计算。示例:两次采样间隔 60 秒,LSN 从 0/1A000000 增至 0/1C800000,差值为 0x2800000 即 40 MB,则速率约为 0.67 MB/s。若该值突然飙升数倍,往往对应批量导入、大批量 UPDATE/DELETE 或索引重建。

检查点频率与 WAL 速率强相关。检查点过于频繁会引发大量脏页刷盘,造成 I/O 抖动;过于稀疏则崩溃恢复时间变长。可通过 pg_stat_bgwriter 观察 checkpoints_timed(定时触发)与 checkpoints_req(请求触发)的比例。健康状态下应以定时检查点为主;若 checkpoints_req 占比明显偏高,说明 max_wal_size 偏小或写入过猛,应结合 checkpoint_completion_target 调整。

缓存命中率:内存效率的体温计

缓存命中率衡量数据块从共享缓冲区读取而非磁盘读取的比例,用 pg_stat_database 的 blks_hit 与 blks_read 计算:命中率 = blks_hit / (blks_hit + blks_read)。OLTP 库正常应高于 99%,低于 95% 通常意味着 shared_buffers 不足、工作集超出内存或存在全表扫描。注意该指标是累计值,应观察一段时间窗口内的增量,而非实例启动以来的总量。

PostgreSQL 关键运行指标健康基线总览 六项指标 → 正常区间 / 异常信号 / 首要排查方向 ① 连接数 正常:占用率 < 70% 关注:70%~85% 异常:> 85% 或 too many clients 排查:active 状态与 wait_event 分布 ② 锁等待 正常:毫秒级瞬时等待 关注:等待会话持续增长 异常:阻塞事务年龄 > 30s 排查:pg_blocking_pids 阻塞链 ③ 复制延迟 正常:同机房 < 1s 关注:跨机房数秒内波动 异常:replay_lag 持续增长 排查:备库 I/O 与回放能力 ④ WAL 生成速率 正常:与业务写入量匹配 关注:短时突增 2~3 倍 异常:持续飙升不回落 排查:批量导入 / 大事务 / 索引重建 ⑤ 检查点频率 正常:以 checkpoints_timed 为主 关注:checkpoints_req 占比上升 异常:频繁请求检查点 + I/O 抖动 排查:max_wal_size / 写入强度 ⑥ 缓存命中率 正常:OLTP > 99% 关注:95%~99% 异常:< 95% 排查:shared_buffers / 全表扫描 指标联动关系 WAL 速率↑ → 检查点请求↑ → I/O 压力↑ → 缓存命中率↓ → 锁等待↑ → 连接堆积 单一指标异常时,应沿此链路向上下游同时排查,避免只治表象

把六项指标串成一条判断链

这六项指标并非孤立。一个典型的连锁反应是:应用发起大批量写入,WAL 生成速率飙升,触发请求式检查点,脏页集中刷盘导致 I/O 压力上升,缓存命中率下降,查询变慢,行锁持有时间变长,锁等待增加,最终连接被占满。因此当某个指标报警时,应沿“WAL 速率 → 检查点 → I/O → 缓存命中 → 锁等待 → 连接数”这条链路向上下游同时排查,而不是只盯住报警的那一个点。

建议为每套生产库建立一张基线记录表,至少包含:指标名称、采集时间窗口、低峰正常值、高峰正常值、告警阈值、采集 SQL。基线应随业务增长每季度复核一次,避免用半年前的旧基线判断当前系统。
不要用实例启动以来的累计值判断当前健康状态。缓存命中率、检查点次数等均为累计计数器,必须取两次采样之间的增量再计算,否则会掩盖正在发生的异常。

掌握这些指标的正常范围与异常信号后,下一步才是把它们接入监控与告警,并在恢复演练中验证:当主库连接耗尽或复制延迟超标时,切换流程是否能在预期时间内完成,数据是否满足 RPO 与 RTO 要求。