当你在真实项目里工作时,可能会有成千上万的用户同时和你的应用打交道。他们会发请求到数据库,添加数据、读取、更新……然后你发现你的服务器开始“呻吟”了。这其实就是你的查询不够优化的信号。有时候那些“纸面上”看起来很美的查询,实际跑起来却能让性能崩溃。这时候 pg_stat_statements 就该上场了。
pg_stat_statements 能让你:
- 追踪慢查询。
- 搞清楚某些查询被执行了多少次。
- 知道它们一共花了多少时间。
- 看到查询的平均执行时间。
- 避免犯下重写整个应用的致命错误!
了解 pg_stat_statements 的结构
扩展激活后,你的数据库里会多出一个特殊视图 pg_stat_statements。这里存着所有已执行查询的数据。先来看看里面都有什么:
SELECT * FROM pg_stat_statements LIMIT 1;
结果可能长这样(简化版):
| query | calls | total_time | rows | shared_blks_read |
|---|---|---|---|---|
| SELECT * FROM students | 500 | 20000 ms | 5000 | 100 |
简单解释一下:
query— SQL 查询本身。calls— 这个查询被执行了多少次。total_time— 这个查询总共花了多少时间。rows— 查询返回了多少行。shared_blks_read— 读取了多少块(如果你没用缓存就会访问磁盘)。
结果分析
现在 pg_stat_statements 已经开了,来看看怎么找慢查询。
最慢的查询
要找出哪些查询最耗时,可以用这个 SQL:
SELECT query, total_time, calls, mean_time
FROM pg_stat_statements
ORDER BY total_time DESC
LIMIT 5;
这里:
mean_time— 单次查询的平均耗时(total_time / calls)。ORDER BY total_time DESC— 按总耗时降序排列。
最常被执行的查询
有时候问题不是慢查询,而是某些查询被执行得太频繁。比如:
SELECT query, calls
FROM pg_stat_statements
ORDER BY calls DESC
LIMIT 5;
查询优化
- 用索引
如果你发现某些字段的查询很慢,先看看这些字段有没有索引。比如你有个 students 表,数据量很大,经常查 last_name 字段。那就该建个索引:
CREATE INDEX idx_students_last_name ON students (last_name);
- 重写查询
假设你发现 SELECT * FROM orders WHERE amount > 1000 这种查询太慢了。大概率你其实不需要“全字段”,只要查需要的列就行:
SELECT order_id, amount FROM orders WHERE amount > 1000;
清空统计数据
有时候你想只看最新的结果(比如优化后),就得清空 pg_stat_statements 里的数据。用这个命令:
SELECT pg_stat_statements_reset();
就像你计算器上的“重置”按钮。执行后统计会重新开始。
查找有问题的查询
假设你是大学的数据库管理员,学生们都在吐槽他们的个人中心加载太慢。你决定查查 pg_stat_statements:
第 1 步:找最慢的查询
SELECT query, total_time, calls, mean_time
FROM pg_stat_statements
ORDER BY total_time DESC
LIMIT 1;
你发现像 SELECT * FROM students WHERE status = 'active' 这样的查询居然要 30 秒。卧槽,得赶紧处理。
第 2 步:检查索引 分析 students 表后你发现 status 字段没索引。赶紧补上:
CREATE INDEX idx_students_status ON students (status);
第 3 步:检查结果 优化后你再查 pg_stat_statements,发现查询只要 0.5 秒了。赢了!
用 pg_stat_statements 常见错误
有时候管理员分析查询时会犯这些错:
- 扩展没激活。如果你忘了在
shared_preload_libraries里加pg_stat_statements,统计数据根本不会收集。 - 忽略索引。查询慢不一定是 SQL 写得烂,可能就是缺了合适的索引。
- 没重置统计。如果你不执行
pg_stat_statements_reset(),老数据会影响你分析现在的情况。
用 pg_stat_statements 就像给数据库装了 GPS:它能告诉你卡在哪个“堵点”,还会给你指路。把这个工具用好,你的数据库性能能提升一大截。
GO TO FULL VERSION