想抓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

相关推荐
小猿姐2 小时前
实测对比:哪款开源 Kubernetes MySQL Operator 最值得用?(2026 深度评测)
数据库·mysql·云原生
一灯架构4 小时前
90%的人答错!一文带你彻底搞懂ArrayList
java·后端
倔强的石头_4 小时前
从 “存得下” 到 “算得快”:工业物联网需要新一代时序数据平台
数据库
Y4090015 小时前
【多线程】线程安全(1)
java·开发语言·jvm
TDengine (老段)5 小时前
TDengine IDMP 可视化 —— 分享
大数据·数据库·人工智能·时序数据库·tdengine·涛思数据·时序数据
布局呆星5 小时前
SpringBoot 基础入门
java·spring boot·spring
风吹迎面入袖凉6 小时前
【Redis】Redisson的可重入锁原理
java·redis
GottdesKrieges6 小时前
OceanBase数据库备份配置
数据库·oceanbase
w6100104666 小时前
cka-2026-ConfigMap
java·linux·cka·configmap