定位 PostgreSQL 慢查询的一个固定流程
每次有人问"数据库慢",我都会按同一个顺序走一遍。顺序很重要,跳过哪一步都容易做无用功。
第一步:先看总量,别看单条
pg_stat_statements 需要提前开,改 postgresql.conf:
shared_preload_libraries = 'pg_stat_statements'
重启后建扩展:
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
然后按总耗时排序:
SELECT calls,
round(total_exec_time::numeric, 1) AS total_ms,
round(mean_exec_time::numeric, 2) AS mean_ms,
rows,
left(query, 80) AS query
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 20;
注意排序用的是 total_exec_time,不是 mean_exec_time。一条 2ms 的查询跑一百万次,比一条 800ms 的查询跑十次更值得优化——但大多数人第一反应是去找那条 800ms 的。
第二步:看单条的执行计划
EXPLAIN (ANALYZE, BUFFERS)
SELECT ...;
BUFFERS 一定要加。没有它你只能看到"慢",不知道为什么慢。shared read 高说明在真读盘,shared hit 高说明是内存命中。
ANALYZE 会真正执行,写操作记得包在事务里回滚。
第三步:对比预估行数和实际行数
这是最容易被忽略的一步。有张表 800 万行,查询条件是两列的组合,走了 seq scan。看执行计划发现 rows=1200 预估,实际返回 24 万行——差了两百倍。
这不是索引的问题,是统计信息太旧了:
ANALYZE orders;
跑完之后计划立刻变了,从 seq scan 换成了 bitmap index scan,耗时从 1.8s 降到 40ms。
顺序总结
pg_stat_statements按总耗时排序,找到真正值得优化的查询EXPLAIN (ANALYZE, BUFFERS)看计划和实际读了多少块- 对比预估行数和实际行数,不一致先
ANALYZE - 以上都正常,才考虑加索引或者改 SQL
很多被叫作"慢查询"的问题,其实是统计信息的问题。上来就加索引,往往只是把问题往后推了几个月。