想抓PostgreSQL里的慢SQL?pg_stat_statements基础黑匣子和pg_stat_monitor时间窗,谁能帮你更准揪出性能小偷?

  1. pg_stat_statements:基础性能统计模块

1.1 什么是 pg_stat_statements?
pg_stat_statements 是 PostgreSQL 官方提供的核心性能监控模块 ,用于跟踪所有 SQL 语句的计划与执行统计信息。它能帮你回答:

  • 哪些 SQL 执行次数最多?
  • 哪些 SQL 总执行时间最长?
  • 哪些 SQL 的缓存命中率最低(IO 开销大)?
  • 某条 SQL 的平均执行时间是多少?

简单来说,它是 PostgreSQL 性能优化的"黑匣子 "------记录所有 SQL 的运行痕迹,帮你定位瓶颈。

1.2 安装与启用
pg_stat_statements 需要预加载 (因为它需要共享内存),安装步骤分 3 步:

步骤 1:修改配置文件

编辑 PostgreSQL.conf(通常在/var/lib/PostgreSQL/17/main/$PGDATA 目录):

复制代码
# 1. 预加载模块(必须)
shared_preload_libraries = 『pg_stat_statements』

# 2. 启用查询 ID 计算(必须,用于唯一标识相同结构的查询)
compute_query_id = on

# 3. 可选配置(根据需求调整)
pg_stat_statements.max = 10000       # 最多跟踪 10000 条不同的 SQL
pg_stat_statements.track = all       # 跟踪顶级+嵌套语句(比如函数内的 SQL)
pg_stat_statements.track_utility = on # 跟踪工具命令(如 CREATE TABLE)

步骤 2:重启 PostgreSQL

修改配置后需要重启数据库使生效:

复制代码
sudo systemctl restart PostgreSQL

步骤 3:创建扩展

登录数据库(如 psql -U postgres),执行以下命令启用扩展:

复制代码
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

1.3 核心配置参数解析

参数作用默认值 pg_stat_statements.max 最多跟踪多少条不同的 SQL(超过则丢弃最不常用的条目)5000pg_stat_statements.track 跟踪范围:top(仅顶级语句)、all(顶级+嵌套)、none(不跟踪)toppg_stat_statements.track_utility 是否跟踪工具命令(如 VACUUMCREATEonpg_stat_statements.track_planning 是否跟踪计划时间(会增加性能开销)offpg_stat_statements.save 重启后是否保留统计信息

参数 作用 默认值
pg_stat_statements.max 最多跟踪多少条不同的SQL(超过则丢弃最不常用的条目) 5000
pg_stat_statements.track 跟踪范围:top(仅顶级语句)、all(顶级+嵌套)、none(不跟踪) top
pg_stat_statements.track_utility 是否跟踪工具命令(如VACUUM、CREATE) on
pg_stat_statements.track_planning 是否跟踪计划时间(会增加性能开销) off
pg_stat_statements.save 重启后是否保留统计信息 on

on

1.4 关键视图与字段说明
pg_stat_statements 提供两个核心视图:

1.4.1 pg_stat_statements:SQL 统计详情

这个视图是性能分析的核心 ,每一行对应一条不同结构的 SQL(用 queryid 标识)。关键字段如下:

字段含义 queryidSQL 的唯一哈希 ID(相同结构的 SQL 哈希值相同)querySQL 文本(常量会被替换为$1$2,比如 SELECT * FROM users WHERE id = $1calls 执行次数 total_exec_time 总执行时间(毫秒,最常用的慢查询指标mean_exec_time 平均执行时间(毫秒)rows 总返回/影响的行数 shared_blks_hit 共享缓存命中次数(越高越好,说明少读磁盘)shared_blks_read 共享缓存未命中次数(需要读磁盘,IO 开销大)stats_since

字段 含义
queryid SQL的唯一哈希ID(相同结构的SQL哈希值相同)
query SQL文本(常量会被替换为$1、$2,比如SELECT * FROM users WHERE id = $1)
calls 执行次数
total_exec_time 总执行时间(毫秒,最常用的慢查询指标)
mean_exec_time 平均执行时间(毫秒)
rows 总返回/影响的行数
shared_blks_hit 共享缓存命中次数(越高越好,说明少读磁盘)
shared_blks_read 共享缓存未命中次数(需要读磁盘,IO开销大)
stats_since 统计开始时间

统计开始时间

示例 :计算缓存命中率(越高越好):

复制代码
SELECT 
  query,
  calls,
  total_exec_time,
  100.0 * shared_blks_hit / NULLIF(shared_blks_hit + shared_blks_read, 0) AS hit_percent
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 5;

1.4.2 pg_stat_statements_info:模块自身统计

这个视图只有 1 行,记录模块的运行状态:

字段含义 dealloc 因超过 pg_stat_statements.max 而丢弃的 SQL 条目数(值大说明 max 太小)stats_reset

字段 含义
dealloc 因超过pg_stat_statements.max而丢弃的SQL条目数(值大说明max太小)
stats_reset 统计信息最后重置时间

统计信息最后重置时间

1.5 实际使用示例

示例 1:找最耗时的前 5 条 SQL

复制代码
SELECT 
  query,
  calls,
  total_exec_time,
  mean_exec_time,
  rows
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 5;

结果解读total_exec_time 最高的 SQL 是性能优化的优先目标(比如总时间 10 秒的 SQL,即使平均时间短,但执行次数多也会拖慢整体性能)。

示例 2:找缓存命中率低的 SQL

复制代码
SELECT 
  query,
  calls,
  100.0 * shared_blks_hit / NULLIF(shared_blks_hit + shared_blks_read, 0) AS hit_percent
FROM pg_stat_statements
WHERE hit_percent < 90  -- 命中率低于 90%
ORDER BY hit_percent ASC;

优化思路 :命中率低说明 SQL 经常读磁盘,可能需要添加索引加大 shared_buffers(数据库缓存)。

示例 3:重置统计信息

如果统计信息太旧(比如测试环境),可以重置:

复制代码
-- 重置所有统计(仅超级用户可执行)
SELECT pg_stat_statements_reset();

-- 重置某条 SQL 的统计(需指定 queryid)
SELECT pg_stat_statements_reset(0, 0, 『1234567890』); -- 0 表示不限制用户/数据库,queryid 替换为实际值
  1. pg_stat_monitor:增强型性能监控工具
    pg_stat_statements 是基础,但有个明显局限------统计是累计的 (比如某条 SQL 的总执行时间是从启动到现在的总和),无法看到时间维度的变化 (比如"最近 1 小时这条 SQL 的执行时间是否变长?")。
    pg_stat_monitor 是 Percona 开发的增强版模块 ,解决了这个问题,适合持续监控

2.1 核心特性(对比 pg_stat_statements

特性 pg_stat_statementspg_stat_monitor 累计统计✅✅按时间窗口统计❌✅(比如每 1 分钟一个窗口)响应时间直方图❌✅(看 SQL

特性 pg_stat_statements pg_stat_monitor
累计统计
按时间窗口统计 ✅(比如每1分钟一个窗口)
响应时间直方图 ✅(看SQL的响应时间分布)
慢查询日志集成 ✅(自动标记慢查询)
更多维度过滤(如用户、数据库) ✅(更细粒度)

的响应时间分布)慢查询日志集成❌✅(自动标记慢查询)更多维度过滤(如用户、数据库)✅✅(更细粒度)

2.2 安装与配置

pg_stat_monitor 需要从 Percona 仓库安装(或编译源码):

复制代码
# 安装 Percona 仓库(以 Debian/Ubuntu 为例)
sudo apt install percona-PostgreSQL-17-pg_stat_monitor

修改 PostgreSQL.conf

复制代码
shared_preload_libraries = 『pg_stat_monitor』  # 替换或新增
pg_stat_monitor.interval = 60                 # 时间窗口大小(秒,默认 60)
pg_stat_monitor.max = 10000                   # 最多跟踪 10000 条 SQL

重启数据库后创建扩展:

复制代码
CREATE EXTENSION IF NOT EXISTS pg_stat_monitor;

2.3 常用查询示例

示例:看最近 1 小时每条 SQL 的平均执行时间

复制代码
SELECT 
  query,
  sum(calls) AS total_calls,
  avg(mean_exec_time) AS avg_mean_exec_time,
  time
FROM pg_stat_monitor
WHERE time >= NOW() - INTERVAL 『1 hour』
GROUP BY query, time
ORDER BY avg_mean_exec_time DESC;
  1. 持续优化工作流:从监控到优化

性能优化不是"一次性操作",而是持续循环 。结合 pg_stat_statements pg_stat_monitor,流程如下:

3.1 步骤 1:定位瓶颈 SQL

pg_stat_statements 总执行时间最长缓存命中率最低 的 SQL;用 pg_stat_monitor 时间维度的性能变化 (比如某条 SQL 的执行时间从 10ms 涨到 100ms)。

3.2 步骤 2:分析执行计划

对瓶颈 SQL 运行 EXPLAIN ANALYZE,看是否缺少索引、是否全表扫描:

复制代码
EXPLAIN ANALYZE SELECT * FROM orders WHERE customer_id = 123;

结果解读 :如果看到 Seq Scan on orders(全表扫描),说明缺少 customer_id 的索引。

3.3 步骤 3:优化 SQL 或索引

比如给 orders 表的 customer_id 添加索引:

复制代码
CREATE INDEX idx_orders_customer_id ON orders(customer_id);

3.4 步骤 4:验证优化效果

优化后,用 pg_stat_statements 重新查询该 SQL 的 total_exec_time shared_blks_read,看是否下降;用 pg_stat_monitor 看时间窗口内的执行时间是否恢复正常。

  1. 最佳实践与注意事项

  2. 定期重置统计信息 :比如每周重置一次(SELECT pg_stat_statements_reset();),避免旧数据干扰分析。

  3. 设置合适的 pg_stat_statements.max :如果 dealloc 值很大(看 pg_stat_statements_info),说明 max 太小,需要增大(比如从 5000 改到 10000)。

  4. 开启 track_planning 谨慎track_planning 会跟踪计划时间,但会增加性能开销,仅在需要分析计划问题时开启。

  5. 权限控制pg_stat_statements query 字段包含 SQL 文本,仅超级用户pg_read_all_stats 角色能看其他用户的 SQL(避免敏感信息泄露)。

  6. 课后 Quiz

问题 1:如何用 pg_stat_statements 找出最耗时的前 3 条 SQL?

复制代码
SELECT query, calls, total_exec_time FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 3;

问题 2:pg_stat_statements.track 设置为 all 会跟踪哪些语句?
答案 :会跟踪顶级语句 (如客户端直接执行的 SQL)和嵌套语句 (如函数或存储过程内的 SQL)。

问题 3:如果 pg_stat_statements_info 中的 dealloc 值很大,说明什么?
答案 :说明 pg_stat_statements.max 设置太小,导致很多 SQL 条目被丢弃,需要增大 max 值。

  1. 常见报错与解决方法

报错 1:ERROR: could not access file 「pg_stat_statements」: No such file or directory
原因 :没有安装 pg_stat_statements 扩展,或没有预加载模块。
解决

  1. 安装扩展(如 apt install PostgreSQL-17-pg-stat-statements)。
  2. 修改 shared_preload_libraries pg_stat_statements 并重启数据库。

报错 2:ERROR: permission denied for function pg_stat_statements_reset
原因 :当前用户没有执行 pg_stat_statements_reset 的权限。
解决

  1. 切换到超级用户(如 postgres)执行。
  2. 给用户授予权限:GRANT EXECUTE ON FUNCTION pg_stat_statements_reset() TO your_user;

报错 3:ERROR: pg_stat_statements must be loaded via shared_preload_libraries
原因 :没有在 PostgreSQL.conf 中预加载 pg_stat_statements
解决 :修改 shared_preload_libraries 并重启数据库。

更多技术专题资料:https://github.com/encode-studio-fe/natural_traffic/wiki/scan_material_yinke0020

相关推荐
lang201509285 小时前
MySQL 8.0.29 及以上版本中 SSL/TLS 会话复用(Session Reuse)
数据库·mysql
Jabes.yang5 小时前
Java面试大作战:从缓存技术到音视频场景的探讨
java·spring boot·redis·缓存·kafka·spring security·oauth2
Query*5 小时前
Java 设计模式——适配器模式进阶:原理深挖、框架应用与实战扩展
java·设计模式·适配器模式
Sirens.5 小时前
Java核心概念:抽象类、接口、Object类深度剖析
java·开发语言·github
Meteors.5 小时前
23种设计模式——中介者模式 (Mediator Pattern)详解
java·设计模式·中介者模式
望获linux5 小时前
【实时Linux实战系列】使用 u-trace 或 a-trace 进行用户态应用剖析
java·linux·前端·网络·数据库·elasticsearch·操作系统
焰火19995 小时前
[Java]基于Spring的轻量级定时任务动态管理框架
java·后端
Seven976 小时前
Springboot 常见面试题汇总
java·spring boot
程序员阿鹏6 小时前
49.字母异位词分组
java·开发语言·leetcode