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(超过则丢弃最不常用的条目) | 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 |
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 | 统计开始时间 |
示例:计算缓存命中率(越高越好):
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 | 统计信息最后重置时间 |
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替换为实际值
2. pg_stat_monitor:增强型性能监控工具
pg_stat_statements是基础,但有个明显局限——统计是累计的(比如某条SQL的总执行时间是从启动到现在的总和),无法看到时间维度的变化(比如“最近1小时这条SQL的执行时间是否变长?”)。
pg_stat_monitor是Percona开发的增强版模块,解决了这个问题,适合持续监控。
2.1 核心特性(对比pg_stat_statements)
| 特性 | 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;
3. 持续优化工作流:从监控到优化
性能优化不是“一次性操作”,而是持续循环。结合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看时间窗口内的执行时间是否恢复正常。
4. 最佳实践与注意事项
- 定期重置统计信息:比如每周重置一次(
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(避免敏感信息泄露)。
5. 课后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值。
6. 常见报错与解决方法
报错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并重启数据库。
7. 参考链接
- PostgreSQL官方文档:
pg_stat_statements模块
www.postgresql.org/docs/17/pgs… - Percona文档:
pg_stat_monitor模块
docs.percona.com/pg-stat-mon… - PostgreSQL配置参数:
compute_query_id
www.postgresql.org/docs/17/run…
往期文章归档
- PostgreSQL的“时光机”MVCC和锁机制是怎么搞定高并发的? - cmdragon's Blog
- PostgreSQL性能暴涨的关键?内存IO并发参数居然要这么设置? - cmdragon's Blog
- 大表查询慢到翻遍整个书架?PostgreSQL分区表教你怎么“分类”才高效
- PostgreSQL 查询慢?是不是忘了优化 GROUP BY、ORDER BY 和窗口函数? - cmdragon's Blog
- PostgreSQL里的子查询和CTE居然在性能上“掐架”?到底该站哪边? - cmdragon's Blog
- PostgreSQL选Join策略有啥小九九?Nested Loop/Merge/Hash谁是它的菜? - cmdragon's Blog
- PostgreSQL新手SQL总翻车?这7个性能陷阱你踩过没? - cmdragon's Blog
- PostgreSQL索引选B-Tree还是GiST?“瑞士军刀”和“多面手”的差别你居然还不知道? - cmdragon's Blog
- 想知道数据库怎么给查询“算成本选路线”?EXPLAIN能帮你看明白? - cmdragon's Blog
- PostgreSQL处理SQL居然像做蛋糕?解析到执行的4步里藏着多少查询优化的小心机? - cmdragon's Blog
- PostgreSQL备份不是复制文件?物理vs逻辑咋选?误删还能精准恢复到1分钟前? - cmdragon's Blog
- 转账不翻车、并发不干扰,PostgreSQL的ACID特性到底有啥魔法? - cmdragon's Blog
- 银行转账不白扣钱、电商下单不超卖,PostgreSQL事务的诀窍是啥? - cmdragon's Blog
- PostgreSQL里的PL/pgSQL到底是啥?能让SQL从“说目标”变“讲步骤”? - cmdragon's Blog
- PostgreSQL视图不存数据?那它怎么简化查询还能递归生成序列和控制权限? - cmdragon's Blog
- PostgreSQL索引这么玩,才能让你的查询真的“飞”起来? - cmdragon's Blog
- PostgreSQL的表关系和约束,咋帮你搞定用户订单不混乱、学生选课不重复? - cmdragon's Blog
- PostgreSQL查询的筛子、排序、聚合、分组?你会用它们搞定数据吗? - cmdragon's Blog
- PostgreSQL数据类型怎么选才高效不踩坑? - cmdragon's Blog
- 想解锁PostgreSQL查询从基础到进阶的核心知识点?你都get了吗? - cmdragon's Blog
- PostgreSQL DELETE居然有这些操作?返回数据、连表删你试过没? - cmdragon's Blog
- PostgreSQL UPDATE语句怎么玩?从改邮箱到批量更新的避坑技巧你都会吗? - cmdragon's Blog
- PostgreSQL插入数据还在逐条敲?批量、冲突处理、返回自增ID的技巧你会吗? - cmdragon's Blog
- PostgreSQL的“仓库-房间-货架”游戏,你能建出电商数据库和表吗? - cmdragon's Blog
- PostgreSQL 17安装总翻车?Windows/macOS/Linux避坑指南帮你搞定? - cmdragon's Blog
- 能当关系型数据库还能玩对象特性,能拆复杂查询还能自动管库存,PostgreSQL凭什么这么香? - cmdragon's Blog
- 给接口加新字段又不搞崩老客户端?FastAPI的多版本API靠哪三招实现? - cmdragon's Blog
- 流量突增要搞崩FastAPI?熔断测试是怎么防系统雪崩的? - cmdragon's Blog
- FastAPI秒杀库存总变负数?Redis分布式锁能帮你守住底线吗 - cmdragon's Blog
- FastAPI的CI流水线怎么自动测端点,还能让Allure报告美到犯规? - cmdragon's Blog
- 如何用GitHub Actions为FastAPI项目打造自动化测试流水线? - cmdragon's Blog
- 如何用Git Hook和CI流水线为FastAPI项目保驾护航? - cmdragon's Blog
- FastAPI如何用契约测试确保API的「菜单」与「菜品」一致?
- 为什么TDD能让你的FastAPI开发飞起来? - cmdragon's Blog
- 如何用FastAPI玩转多模块测试与异步任务,让代码不再“闹脾气”? - cmdragon's Blog
- 如何在FastAPI中玩转“时光倒流”的数据库事务回滚测试?
- 如何在FastAPI中优雅地模拟多模块集成测试? - cmdragon's Blog
- 多环境配置切换机制能否让开发与生产无缝衔接? - cmdragon's Blog
- 如何在 FastAPI 中巧妙覆盖依赖注入并拦截第三方服务调用? - cmdragon's Blog
- 为什么你的单元测试需要Mock数据库才能飞起来? - cmdragon's Blog
- 如何在FastAPI中巧妙隔离依赖项,让单元测试不再头疼? - cmdragon's Blog
- 如何在FastAPI中巧妙隔离依赖项,让单元测试不再头疼? - cmdragon's Blog
- 测试覆盖率不够高?这些技巧让你的FastAPI测试无懈可击! - cmdragon's Blog
- 为什么你的FastAPI测试覆盖率总是低得让人想哭? - cmdragon's Blog
- 如何让FastAPI测试不再成为你的噩梦? - cmdragon's Blog
- FastAPI测试环境配置的秘诀,你真的掌握了吗? - cmdragon's Blog
- 全链路追踪如何让FastAPI微服务架构的每个请求都无所遁形? - cmdragon's Blog
- 如何在API高并发中玩转资源隔离与限流策略? - cmdragon's Blog
免费好用的热门在线工具
- Mermaid 在线编辑器 - 应用商店 | By cmdragon
- 数学求解计算器 - 应用商店 | By cmdragon
- 智能提词器 - 应用商店 | By cmdragon
- 魔法简历 - 应用商店 | By cmdragon
- Image Puzzle Tool - 图片拼图工具 | By cmdragon
- 字幕下载工具 - 应用商店 | By cmdragon
- 歌词生成工具 - 应用商店 | By cmdragon
- 网盘资源聚合搜索 - 应用商店 | By cmdragon
- ASCII字符画生成器 - 应用商店 | By cmdragon
- JSON Web Tokens 工具 - 应用商店 | By cmdragon
- Bcrypt 密码工具 - 应用商店 | By cmdragon
- GIF 合成器 - 应用商店 | By cmdragon
- GIF 分解器 - 应用商店 | By cmdragon
- 文本隐写术 - 应用商店 | By cmdragon
- CMDragon 在线工具 - 高级AI工具箱与开发者套件 | 免费好用的在线工具
- 应用商店 - 发现1000+提升效率与开发的AI工具和实用程序 | 免费好用的在线工具
- CMDragon 更新日志 - 最新更新、功能与改进 | 免费好用的在线工具
- 支持我们 - 成为赞助者 | 免费好用的在线工具
- AI文本生成图像 - 应用商店 | 免费好用的在线工具
- 临时邮箱 - 应用商店 | 免费好用的在线工具
- 二维码解析器 - 应用商店 | 免费好用的在线工具
- 文本转思维导图 - 应用商店 | 免费好用的在线工具
- 正则表达式可视化工具 - 应用商店 | 免费好用的在线工具
- 文件隐写工具 - 应用商店 | 免费好用的在线工具
- IPTV 频道探索器 - 应用商店 | 免费好用的在线工具
- 快传 - 应用商店 | 免费好用的在线工具
- 随机抽奖工具 - 应用商店 | 免费好用的在线工具
- 动漫场景查找器 - 应用商店 | 免费好用的在线工具
- 时间工具箱 - 应用商店 | 免费好用的在线工具
- 网速测试 - 应用商店 | 免费好用的在线工具
- AI 智能抠图工具 - 应用商店 | 免费好用的在线工具
- 背景替换工具 - 应用商店 | 免费好用的在线工具
- 艺术二维码生成器 - 应用商店 | 免费好用的在线工具
- Open Graph 元标签生成器 - 应用商店 | 免费好用的在线工具
- 图像对比工具 - 应用商店 | 免费好用的在线工具
- 图片压缩专业版 - 应用商店 | 免费好用的在线工具
- 密码生成器 - 应用商店 | 免费好用的在线工具
- SVG优化器 - 应用商店 | 免费好用的在线工具
- 调色板生成器 - 应用商店 | 免费好用的在线工具
- 在线节拍器 - 应用商店 | 免费好用的在线工具
- IP归属地查询 - 应用商店 | 免费好用的在线工具
- CSS网格布局生成器 - 应用商店 | 免费好用的在线工具
- 邮箱验证工具 - 应用商店 | 免费好用的在线工具
- 书法练习字帖 - 应用商店 | 免费好用的在线工具
- 金融计算器套件 - 应用商店 | 免费好用的在线工具
- 中国亲戚关系计算器 - 应用商店 | 免费好用的在线工具
- Protocol Buffer 工具箱 - 应用商店 | 免费好用的在线工具
- IP归属地查询 - 应用商店 | 免费好用的在线工具
- 图片无损放大 - 应用商店 | 免费好用的在线工具
- 文本比较工具 - 应用商店 | 免费好用的在线工具
- IP批量查询工具 - 应用商店 | 免费好用的在线工具
- 域名查询工具 - 应用商店 | 免费好用的在线工具
- DNS工具箱 - 应用商店 | 免费好用的在线工具
- 网站图标生成器 - 应用商店 | 免费好用的在线工具
- XML Sitemap