PostgreSQL自带统计视图只能看到系统级等待,难以直接定位是哪条业务SQL在消耗资源。pg_stat_statements作为官方贡献扩展,通过在内核执行层挂载计数器,把每一条经过规划器的语句按文本归一化后记录调用频次、累计耗时、共享块命中与脏块数量。它不依赖日志采样,也不会因为log_min_duration设置漏掉执行快但次数极多的语句,因此是排查性能瓶颈的首选工具。理解它的工作原理与参数边界,比盲目加索引更有价值。

扩展的加载与基础配置
要让pg_stat_statements生效,第一步并不是直接建扩展,而是必须把它加入shared_preload_libraries。原因在于该扩展需要在数据库启动阶段从共享内存中预留一块固定大小的哈希区,用来存放语句统计条目。如果仅用CREATE EXTENSION而不预加载,视图虽然能建出来,但所有计数都会是空。修改postgresql.conf后必须重启实例,这是与多数普通扩展不同的地方。
配置项里最关键是pg_stat_statements.max,它决定哈希表可容纳多少条不同语句指纹,默认五千在 busy 业务库往往不够,容易把早期高频SQL挤出。另一个参数是track,可选top、all、none,top只统计顶层调用,all会把嵌套在函数内的SQL也拆出来,后者更细但开销略大。以下片段展示最小化配置:
-- postgresql.conf 片段 shared_preload_libraries = 'pg_stat_statements' pg_stat_statements.max = 10000 pg_stat_statements.track = 'top' pg_stat_statements.track_utility = on -- 重启后建扩展 CREATE EXTENSION pg_stat_statements;
建完扩展后,系统会多出一张名为pg_stat_statements的视图。它不占用磁盘,数据只活在内核内存,实例重启即清零,除非你定期落表归档。很多新手误以为它是普通表,用vacuum去优化,其实完全没必要。视图字段中calls、total_exec_time、shared_blks_hit是最常用三列,分别揭示请求量、绝对成本与缓存利用。
核心视图字段与排序排查法
面对成百上千条记录,直接全表扫描没有意义。通常先按总执行时间倒序,找出真正拖慢系统的少数语句。下面例子排出耗时前三,并顺带显示平均单次耗时,帮助区分“慢而少”与“快但巨量”两类问题:
SELECT query,
calls,
round(total_exec_time::numeric, 1) AS total_ms,
round((total_exec_time / calls)::numeric, 2) AS avg_ms,
shared_blks_hit
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 3;
如果某条SQL的avg_ms很低但calls极高,说明它单条不慢却被循环调用,优化方向应是批处理或合并事务,而不是建索引。反之avg_ms上千且calls不多,多半是缺失索引或统计信息过期。视图里的rows字段返回平均返回行数,与shared_blks_read对比能看出是否发生大量磁盘读,从而判断是否该扩容内存或调大shared_buffers。
除了时间维度,还需要关注temp_blks_written。当该值持续增长,意味着语句频繁使用临时文件排序或哈希,往往受work_mem限制。此时盲目调大全局work_mem有内存溢出风险,更稳妥的是针对会话级SET局部提高。通过这些字段交叉分析,能把模糊的“数据库慢”翻译成具体的“某SQL因排序落盘”的可执行结论。
生产环境中的陷阱与清理策略
一个常见误区是认为开启扩展后就可以永久依赖它做审计。实际上共享内存条目在达到pg_stat_statements.max后采用LRU淘汰,老语句统计会丢失,因此长时间不清理也可能看不全历史。官方提供pg_stat_statements_reset()函数,可在版本发布或压测前后主动清零,保证对比干净。注意该函数不带参数时会清空全部库统计,在多租户实例上需评估影响。
-- 发布前清理,仅保留对照基线 SELECT pg_stat_statements_reset(); -- 观察某业务用户产生的负载 SELECT query, calls, total_exec_time FROM pg_stat_statements WHERE userid = (SELECT oid FROM pg_roles WHERE rolname = 'app_user') ORDER BY total_exec_time DESC;
另一个隐患是track_utility开启后,DDL也会被计数,若系统有大量自动建表脚本,会快速占满哈希区,挤走SELECT统计。此时应设track_utility = off,或把max调得更高。同时扩展本身有微小固定开销,在每秒数十万轻量查询的极限场景下,建议用采样或只开top级跟踪,避免成为瓶颈。
最后,不要把pg_stat_statements当成执行计划工具。它只给聚合指标,不揭示为何慢。确认可疑SQL后,仍需借助EXPLAIN (ANALYZE, BUFFERS)看真实计划。将两者结合:扩展负责“找谁慢”,执行计划负责“为何慢”,才能形成完整的PostgreSQL性能监控闭环。定期把Top SQL落表,还能做周环比,提前发现慢查询回归。
pg_stat_statementsPostgreSQL性能监控修改时间:2026-08-16 12:30:29