九月 4, 2025
摘要:在本教程中,我们将提供一些可用于 pg_stat_statements 的示例查询语句,为应对各类场景需求提供起点。
目录
降低系统整体负载
若你希望降低系统的整体负载,一个很好的切入点是按 “总耗时” 分析查询语句,示例如下:
select
(total_exec_time + total_plan_time)::int as total_time,
total_exec_time::int,
total_plan_time::int,
mean_exec_time::int,
calls,
query
from
pg_stat_statements
order by
total_time desc
limit 50;
该查询会返回 “所有调用中总耗时最长的 50 条查询”。这意味着,执行频率极高的快速查询,可能会排在执行频率低的慢速查询之前。这种排序方式能较好地反映出 “哪些查询占用了最多的系统资源”!
在这里,我们将时间统计值转换为了整数(这样可将时间精确到毫秒级别查看),你也可以移除转换逻辑,或采用更灵活的方式展示这些时间数据。
若你使用的是 PostgreSQL 12 及更早版本,将会无法获取 “规划时间” 相关统计数据,并且需要将total_exec_time
和mean_exec_time
分别替换为total_time
和mean_time
。
若你使用的是 PostgreSQL 13 及更新版本,且发现total_plan_time
列全为 0,可检查 pg_stat_statements.track_planning 参数(该参数默认关闭,开启后才能统计规划时间)。
加速最慢的查询
若用户反馈出现性能问题,且你的核心目标是 “加速耗时最长的查询”,可以筛选 “平均耗时” 最长的查询,示例如下:
select
(mean_exec_time + mean_plan_time)::int as mean_time,
mean_exec_time::int,
mean_plan_time::int,
calls,
query
from
pg_stat_statements
--where
-- calls > 1
-- and userid = 99999
order by
mean_time desc
limit 50;
该查询与上一个示例非常相似,同样需注意不同 PostgreSQL 版本的参数差异!
注释掉的 where
子句提供了 “减少结果干扰项” 的筛选思路。按用户userid
筛选:可排除那些 “对性能敏感度低的用户” 发起的慢速查询。限制calls > 1
:若存在用户执行无需优化的 “一次性慢速查询”,可通过该条件排除。
减少 I/O 开销
理解系统资源占用的另一个角度是 “缓冲区使用情况”。如果你知道缓冲区统计数据,这些数据不仅对查询优化极有帮助,也能用于分析整体工作负载。
你可以根据需求调整缓冲区统计数据的求和与排序方式,我们在这里将所有缓冲区统计项求和,用这个 “粗略指标” 来近似代表查询的 “总工作量”。这种方式虽不完美,但实践证明是个不错的分析起点:
select
shared_blks_hit + shared_blks_read + shared_blks_dirtied + shared_blks_written + local_blks_hit + local_blks_read + local_blks_dirtied + local_blks_written + temp_blks_read + temp_blks_written as total_buffers,
(total_exec_time + total_plan_time)::int as total_time,
calls,
shared_blks_hit as sbh,
shared_blks_read as sbr,
shared_blks_dirtied as sbd,
shared_blks_written as sbw,
local_blks_hit as lbh,
local_blks_read as lbr,
local_blks_dirtied as lbd,
local_blks_written as lbw,
temp_blks_read as tbr,
temp_blks_written as tbr,
query
from
pg_stat_statements
order by
total_buffers desc
limit 50;
你也可以将上述缓冲区统计项与前两个查询结合使用,例如:查看 “按总耗时排序的查询” 各自的total_buffers
(总缓冲区操作数)。
在此场景下,我们查看的是 “块数量” 形式的统计列;若你希望按 “字节” 查看,可使用pg_size_pretty()
函数。使用时需注意:需将块数量乘以数据库块大小(默认 8192 字节)。
调整 JIT 即时编译设置
从 PostgreSQL 15 版本开始,pg_stat_statements 新增了 JIT 编译统计数据(同时还新增了 I/O 时间统计)。很多用户可能会遇到 “JIT 编译触发过早” 的问题,这不仅对查询无益处,甚至会拖累查询性能和整体工作负载。
以下示例查询可用于筛选 “JIT 编译耗时占比最高” 的查询(JIT 耗时占查询总耗时的百分比):
select
((jit_generation_time + jit_inlining_time + jit_optimization_time + jit_emission_time)/(total_exec_time + total_plan_time)) as jit_total_time_percent,
calls,
jit_functions,
jit_generation_time,
jit_inlining_count,
jit_inlining_time,
jit_optimization_count,
jit_optimization_time,
jit_emission_count,
jit_emission_time,
query
from
pg_stat_statements
order by
jit_total_time_percent desc
limit 50;
该查询会按 “JIT 编译耗时占查询总耗时的百分比” 降序排列查询语句。通常来说,占比越高,说明 JIT 越可能 “无益处”,但这至少为你提供了 “全系统排查 JIT 问题” 的起点。
总结
pg_stat_statements 既可用于 “系统级性能优化”,也能辅助解决 “单次性能问题”。希望这些示例能为你提供一些思路。