2. 关键性能指标解析

PostgreSQL 的监控体系里,指标数量很多,但真正能支撑容量规划与故障定位的核心指标其实集中在少数几个维度:吞吐量(TPS/QPS)、并发压力(连接数)、内存效率(缓存命中率)、并发冲突(锁等待)以及响应时间(事务提交延迟)。理解这些指标的含义、采集方式和健康阈值,是后续建立监控看板与容量模型的基础。

吞吐量:TPS 与 QPS 的区别与采集

TPS(Transactions Per Second)指每秒完成的事务数,QPS(Queries Per Second)指每秒执行的 SQL 语句数。两者关系并非固定比例:一个事务可能包含多条 SQL,因此 QPS 通常大于 TPS。PostgreSQL 通过统计视图 pg_stat_database 累计事务提交与回滚次数,通过 pg_stat_statements 扩展累计语句执行次数。采集时对累计值做差分即可得到瞬时速率:

-- 事务提交速率(TPS)
SELECT datname,
       xact_commit,
       xact_rollback,
       round((xact_commit - lag(xact_commit) OVER (ORDER BY now()))::numeric
             / extract(epoch FROM now() - lag(now()) OVER (ORDER BY now())), 2) AS tps
FROM pg_stat_database
WHERE datname = 'appdb';

-- 语句执行速率(QPS),需先启用 pg_stat_statements
SELECT sum(calls) AS total_calls
FROM pg_stat_statements;

健康阈值没有绝对标准,应结合业务基线判断。经验上,若 TPS 长期低于历史峰值的 30% 而连接数正常,往往意味着上游流量下降或应用层阻塞;若 TPS 骤降同时锁等待升高,则更可能是数据库内部争用。回滚率(xact_rollback / (xact_commit + xact_rollback))建议控制在 1% 以内,超过 5% 需排查应用逻辑或约束冲突。

连接数:活跃连接与空闲连接的区分

连接数本身不是问题,问题在于连接的状态分布。PostgreSQL 每个连接对应一个后端进程,max_connections 默认 100,但真正消耗资源的是处于 active 状态的连接。通过 pg_stat_activity 可以按状态统计:

SELECT state, count(*)
FROM pg_stat_activity
WHERE backend_type = 'client backend'
GROUP BY state;

健康阈值建议:活跃连接数不超过 max_connections 的 70%,空闲连接(idle)若长期占比超过 60%,说明连接池配置过大或应用未及时释放。更关键的是 idle in transaction 状态,它持有事务快照,会阻碍 vacuum 回收死元组,应尽量控制在个位数。若连接数接近上限,新连接会报 FATAL: sorry, too many clients already,此时应优先检查连接池而非直接调大 max_connections。

缓存命中率:shared_buffers 的效率指标

缓存命中率反映数据块从共享缓冲区读取而非从磁盘读取的比例,计算方式为:

SELECT datname,
       blks_hit,
       blks_read,
       round(blks_hit::numeric / nullif(blks_hit + blks_read, 0) * 100, 2) AS hit_ratio
FROM pg_stat_database
WHERE datname = 'appdb';

对于 OLTP 系统,命中率长期低于 95% 通常意味着 shared_buffers 偏小或存在大量全表扫描。但要注意,命中率是累计值,重启后会重置,且高命中率不等于高性能——如果查询本身返回大量行,即使全部命中缓冲区,CPU 和网络仍是瓶颈。建议将命中率与 pg_stat_statements 中的 shared_blks_read 结合观察,定位具体是哪些语句在制造磁盘读。

锁等待:阻塞链的识别

锁等待直接体现并发冲突。PostgreSQL 提供 pg_locks 与 pg_stat_activity 联合查询,可以找出谁在等锁、谁持有锁:

SELECT w.query AS waiting_query,
       l.mode AS waiting_mode,
       b.query AS blocking_query,
       b.pid AS blocking_pid
FROM pg_stat_activity w
JOIN pg_locks l ON l.pid = w.pid AND NOT l.granted
JOIN pg_locks bl ON bl.locktype = l.locktype
  AND bl.database IS NOT DISTINCT FROM l.database
  AND bl.relation IS NOT DISTINCT FROM l.relation
  AND bl.granted
JOIN pg_stat_activity b ON b.pid = bl.pid;

健康阈值:单次锁等待超过 1 秒即应告警,超过 5 秒需人工介入。若频繁出现 lock_timeout 触发,说明业务 SQL 缺少索引导致行锁升级为表锁,或事务边界过长。建议在应用侧设置 lock_timeout = '3s' 与 statement_timeout,避免单个慢事务拖垮整条链路。

事务提交延迟:端到端响应时间

事务提交延迟指从客户端发起 COMMIT 到收到确认的时间,它综合了 WAL 写入、fsync、复制同步等因素。PostgreSQL 本身不直接记录该指标,通常通过 pg_stat_statements 的 total_exec_time 近似,或在应用层埋点采集。若使用同步复制,pg_stat_replication 中的 write_lag、flush_lag、replay_lag 可反映备库延迟对提交的影响。

健康阈值:OLTP 场景下 P95 提交延迟建议低于 50ms,P99 低于 200ms。若 P99 突然抬升而 TPS 未变,常见原因是 WAL 写入磁盘抖动、checkpoint 集中触发或同步复制备库响应变慢。此时应检查 pg_stat_bgwriter 中的 checkpoints_req 与 buffers_backend,判断是否因 max_wal_size 过小导致频繁 checkpoint。

PostgreSQL 核心性能指标关系图 吞吐量 · 连接数 · 缓存命中率 · 锁等待 · 提交延迟 客户端 / 应用 连接池 · 事务边界 连接数 active / idle / idle in txn SQL 执行 TPS / QPS 共享缓冲区 缓存命中率 ≥ 95% 磁盘 / WAL fsync · checkpoint 锁等待 阻塞链 · lock_timeout 事务提交延迟(端到端) P95 < 50ms · P99 < 200ms · 受 WAL 写入与同步复制影响 健康阈值速查: 回滚率 < 1% | 活跃连接 < 70% max_connections | 缓存命中率 ≥ 95% | 锁等待 < 1s | 提交延迟 P95 < 50ms

指标联动:不要孤立看单个数字

这些指标之间存在明确的因果链。连接数激增会推高活跃事务数,进而增加锁冲突概率;锁等待升高会拉长事务持有时间,导致提交延迟上升;提交延迟上升又会让应用层重试或堆积,进一步推高连接数。缓存命中率下降则直接增加磁盘 I/O,拖慢 WAL 写入,同样反映在提交延迟上。因此,排障时应先看提交延迟是否异常,再顺着连接数、锁等待、缓存命中率逐层下钻,而不是只盯住某一个指标。

建议将上述五个指标以 10 秒为采集间隔写入时序库,并保留至少 30 天。容量规划时,用 P95 而非平均值评估资源水位,因为平均值会掩盖尖峰,而尖峰才是触发故障的直接原因。

采集方式与工具选择

轻量场景可直接用 pg_stat_* 视图配合定时 SQL 采集;生产环境更推荐 pg_stat_statements + Prometheus 的 postgres_exporter,后者已内置上述大部分指标的采集规则。若需要更细的等待事件分析,可启用 pg_stat_activity 的 wait_event_type 与 wait_event 字段,结合 pg_locks 定位具体阻塞点。无论采用哪种方式,都应确保采集本身不成为负担——避免高频全表扫描 pg_stat_activity,必要时用 pg_stat_statements 的聚合结果替代。

注意:pg_stat_database 的累计值在实例重启后会清零,做差分计算时需处理重置边界,否则会得到负值或异常尖峰。建议在采集端记录上次值并判断是否发生重置。

掌握这五类指标的含义、采集与阈值后,下一步就是将它们组织成可观测的监控看板,并据此建立容量基线。指标本身不会说话,只有把它们放在同一时间轴上对照,才能看清数据库真实的运行状态。