pg_stat_activity是唯一能实时查看“此刻卡在哪条sql”的活快照,需过滤state='active'并计算now()-query_start,配合wait_event判断锁等待;pg_stat_statements则提供历史累计指标,二者必须配对使用才能准确定位慢查询根因。

直接看 pg_stat_activity 和 pg_stat_statements 就能拿到绝大多数慢查询线索,但必须配对使用、不能只查一个;系统指标则需叠加 pg_stat_bgwriter、pg_stat_database 等视图交叉验证,单点数据容易误判。
查当前正在跑的慢 SQL:用 pg_stat_activity 过滤 active + duration
这个视图是唯一能实时看到“此刻卡在哪条语句上”的入口,不是历史统计,而是活的快照。
-
state = 'active'是必要条件,但不够——还要结合now() - query_start算出真实运行时长 - 别漏掉
wait_event_type和wait_event:如果显示Lock+relation,说明被锁住了,不是 SQL 本身慢 -
pid pg_backend_pid()必须加,否则自己查自己的查询会把自己刷进去,干扰判断 - 如果
query字段被截断(虽然默认是text类型,但某些客户端或中间件可能做限制),用left(query, 200)提取前缀辅助识别模式
查高频/高耗时 SQL:用 pg_stat_statements 看累计指标
它不告诉你“现在哪条在跑”,而是告诉你“过去谁最拖后腿”,适合归因和长期优化。
- 必须先确认插件已启用:
shared_preload_libraries包含'pg_stat_statements',且 PostgreSQL 已重启 - 每个数据库都要单独执行
CREATE EXTENSION pg_stat_statements,否则查不到数据 - 重点关注三个字段组合:
total_exec_time(总耗时)、mean_exec_time(平均单次耗时)、calls(调用次数)——高total_exec_time但低calls,说明单次极慢;高calls但中等mean_exec_time,说明是高频小慢查询,累积影响大 -
shared_blks_read显著高于shared_blks_hit?说明缓存命中差,优先看索引或work_mem是否不足
查系统级健康信号:组合 pg_stat_bgwriter 和 pg_stat_database
这些视图不直接报慢 SQL,但能解释“为什么慢”——比如检查点风暴、WAL 压力、连接数打满等底层原因。
-
pg_stat_bgwriter.checkpoints_req持续高于checkpoints_timed?说明检查点太频繁被动触发,要调大max_wal_size或提高checkpoint_completion_target -
pg_stat_database.xact_commit和xact_rollback比值骤降?可能是事务异常增多,配合pg_stat_activity.state = 'idle in transaction (aborted)'查具体会话 -
pg_stat_database.blks_read/blks_hit计算缓存命中率,低于 95% 就该警惕——但注意:这是库级别汇总,不能定位到具体表或查询 - 别只看单个数据库的
pg_stat_database,用SELECT datname, numbackends FROM pg_stat_database WHERE datname NOT IN ('template0', 'template1')快速发现连接数异常突增的库
容易忽略的权限和采样陷阱
pg_stat_statements 默认只统计 superuser 的查询;非 superuser 用户执行的语句,若未显式授权,query 字段会显示为 <insufficient privilege></insufficient>,但其他指标(如耗时、调用次数)仍可见。这不是数据丢了,是权限遮蔽。
- 如果需要跨用户查看完整 SQL 文本,得给监控账号授予
pg_read_all_data角色,或在创建扩展后手动GRANT SELECT ON pg_stat_statements TO monitor_role -
pg_stat_statements.max设得太小(比如默认 5000),高频更新场景下老 SQL 会被挤出,导致“明明刚慢过,一刷新就不见了”——生产建议设为 10000 起步 -
pg_stat_activity中的query是实时抓取的,但某些 ORM 或连接池(如 PgBouncer 的 transaction pooling 模式)会复用连接、隐藏真实语句,这时看到的可能是 prepare / bind 协议帧,而非原始 SQL










