数据库急诊室开篇:一张慢 SQL 分诊表
这篇是我在博客园的第一篇。
但我不是来写 SQL 教程的。我想把我排查慢 SQL 的顺序摊开,做成一张表。下次再遇到“这条 SQL 为什么慢”,你可以先照着表走一遍,再决定要不要加索引、改 SQL、调参数。
我是阿哲,八年数据库运维和优化,金融行业甲方出身,后来做独立顾问,集中式和分布式都碰过。这个号叫「数据库急诊室」,只聊跑过生产的东西;给结论之前,先给数字。
一、为什么叫“急诊室”
急诊室的工作方法只有三步:先分诊,再检查,最后才动手。
数据库也一样。一条 SQL 慢,可能是会话在等锁,可能是索引没参与访问,也可能是排序和聚合太贵。不先分清类型,改索引、改 SQL、调参数,都是在猜;猜对了是运气,猜错了要返工。
我刚入行那几年,看到慢 SQL 先翻索引。后来发现,索引只是「读」这一类里的一个变量;「等」和「算」两类,根本不在索引上。从那以后,我先看现象,再提假设,最后用数据验证。
二、一张分诊表
下面的视图名以 PostgreSQL 兼容库为例;别的库叫法不同,分诊顺序不变。
| 类型 | 典型现象 | 先看什么 | 常用视图 / 命令 | 常见误判 |
|---|---|---|---|---|
| 等 | 会话挂着不返回,CPU 和磁盘都不忙 | 阻塞链、等待事件、连接池排队 | pg_stat_activity 的 wait_event、pg_locks 的 granted、连接池监控 |
把等待当成 SQL 写得重 |
| 读 | 扫描行数远大于返回行数,回表多 | 实际行数、访问路径、过滤条件 | EXPLAIN (ANALYZE, BUFFERS) 的 actual rows、Buffers、Heap Fetches |
看到全表扫描就建索引 |
| 算 | 排序、聚合、去重、函数占用高 | 计划节点耗时、临时空间读写 | Sort、HashAggregate、GroupAggregate、temp read/written |
以为数据量大,只能加机器 |
这张表不是诊断书,是分诊台。它只回答“先去哪个科室”,不回答“具体是什么病”。
三、三个问题
第一个问题:会话卡在哪个等待事件上?
-- 当前会话在等什么(PostgreSQL 兼容库示例)
select pid, state, wait_event_type, wait_event, query
from pg_stat_activity
where state <> 'idle';
看 wait_event,看 pg_locks 里有没有 granted = false 的锁,再看连接池有没有打满。如果是在等,先处理等待,不要改 SQL。
第二个问题:扫描行数和返回行数差多少?
explain (analyze, buffers)
select ...
from your_table
where ...;
看 actual rows、Buffers、Heap Fetches,看条件是 Index Cond 还是 Filter。如果读得太多,先看访问路径和统计信息,不要盲目加索引。
第三个问题:时间花在哪个计划节点上?
看计划里的 Sort、HashAggregate、GroupAggregate、temp read/written,看 GROUP BY、UNION ALL、DISTINCT、窗口函数有没有把过滤挡在外面。
如果是算得太久,先改逻辑层级,再谈参数。
四、十分钟动作清单
- 确认现象。 偶发还是持续,一条 SQL 还是整体变慢,业务侧能不能复现。
- 看等待。 有没有锁等待、连接池排队、IO 等待。先排除外部因素。
- 看读取。 拿执行计划,看扫描行数、访问路径、过滤条件。
- 看计算。 排序、聚合、临时空间、函数调用。
- 定下一步。 改 SQL、补统计信息、加索引,还是先处理锁和连接池。
- 想好验证。 每改一步,先想好怎么证明它有效,别用“感觉快了”交差。
五、两个常见误判
误判一:看到全表扫描就加索引。
全表扫描有时是合理的:表很小、过滤条件命中大部分数据、或者没有合适的索引可用。要看“读了多少行、返回了多少行”,不是“有没有走索引”。
误判二:看到执行计划正常就放心。
统计信息不准、参数嗅探、缓存命中率变化、数据分布倾斜,都可能让计划“看起来正常”,实际很慢。要看实际行数和估算行数的差异。
六、分诊不够的时候
分诊只能帮你判断方向,不能替代完整分析。下面几种情况,分诊之后还要往下挖:
- 单条 SQL 不慢,整体吞吐上不去:看连接池、网络、应用层重试、批量任务叠加;
- 改完 SQL 快了,别的地方慢了:看锁范围、日志量、IO 峰值、下游压力;
- 生产不能复现:先补观测,不要靠猜。
七、关于我的这个号
四个方向:慢 SQL 解剖、事故复盘、急诊箱脚本、金融场景里的数据库。
会写:跑过生产的东西;有前后对比的优化;能复现的慢查询;能跑通的脚本。
不会写:没闭环的悬案;客户名、系统名、可识别的项目信息;没测过的性能倍数;品牌排序和选型推荐(首月)。
八、最后
这张分诊表,你可以直接抄走,按自己的库改视图名。如果你有更细的判断项,欢迎在评论区补充。
你最近一次慢 SQL,是等、读、还是算?带一句现象就行,不用带客户名。
同样的现象可能有别的根因,欢迎带数字来讨论。

浙公网安备 33010602011771号