- 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
是否跟踪工具命令(如 VACUUM
、CREATE
)onpg_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
标识)。关键字段如下:
字段含义 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
字段 |
含义 |
---|---|
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 替换为实际值
- 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;
- 持续优化工作流:从监控到优化
性能优化不是"一次性操作",而是持续循环 。结合 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
看时间窗口内的执行时间是否恢复正常。
-
最佳实践与注意事项
-
定期重置统计信息 :比如每周重置一次(
SELECT pg_stat_statements_reset();
),避免旧数据干扰分析。 -
设置合适的
pg_stat_statements.max
:如果dealloc
值很大(看pg_stat_statements_info
),说明max
太小,需要增大(比如从 5000 改到 10000)。 -
开启
track_planning
谨慎 :track_planning
会跟踪计划时间,但会增加性能开销,仅在需要分析计划问题时开启。 -
权限控制 :
pg_stat_statements
的query
字段包含 SQL 文本,仅超级用户 和pg_read_all_stats
角色能看其他用户的 SQL(避免敏感信息泄露)。 -
课后 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:ERROR: could not access file 「pg_stat_statements」: No such file or directory
原因 :没有安装 pg_stat_statements
扩展,或没有预加载模块。
解决:
- 安装扩展(如
apt install PostgreSQL-17-pg-stat-statements
)。 - 修改
shared_preload_libraries
为pg_stat_statements
并重启数据库。
报错 2:ERROR: permission denied for function pg_stat_statements_reset
原因 :当前用户没有执行 pg_stat_statements_reset
的权限。
解决:
- 切换到超级用户(如
postgres
)执行。 - 给用户授予权限:
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