本文目录导读:

快速排查慢查询问题,核心思路是先定位“元凶”SQL,再分析其执行计划,不要盲目加索引或改代码。
以下是分步骤的快速排查流程:
第一阶段:快速定位“元凶”SQL(秒级)
这是最关键的一步,需要直接从数据库层面抓取当前正在执行或近期执行缓慢的SQL。
方法1:查看当前正在运行的慢查询(最直接)
- MySQL:
-- 查看当前正在执行的所有线程,重点关注 Time 列(执行时间) SHOW FULL PROCESSLIST; -- 或者在 MySQL 5.7+ 中使用 performance_schema SELECT * FROM sys.session WHERE command != 'Sleep' AND time > 5 ORDER BY time DESC;
- PostgreSQL:
SELECT pid, now() - pg_stat_activity.query_start AS duration, query, state FROM pg_stat_activity WHERE state != 'idle' AND now() - pg_stat_activity.query_start > interval '5 seconds' ORDER BY duration DESC;
方法2:开启慢查询日志(事后分析)
- MySQL:
-- 查看当前是否开启及位置 SHOW VARIABLES LIKE 'slow_query_log%'; SHOW VARIABLES LIKE 'long_query_time'; -- 默认10秒,建议设为1秒或0.5秒
- 生产环境建议临时开启,使用
mysqldumpslow或pt-query-digest分析日志文件,找到执行频率高、耗时长的SQL。
- 生产环境建议临时开启,使用
方法3:操作系统层面(最后一根稻草)
- 如果数据库配置了连接池,且SQL都很快,但整体响应慢,可能是操作系统资源瓶颈。
- 常用命令:
top(CPU/内存)、iostat -x 1(磁盘IO,重点关注 await、%util)、vmstat 1(上下文切换、等待队列)。
第二阶段:分析SQL执行计划(分钟级)
找到慢SQL后,使用 EXPLAIN 分析为什么慢。
- MySQL:
EXPLAIN [慢SQL];
- PostgreSQL:
EXPLAIN (ANALYZE, BUFFERS) [慢SQL];
关键指标(你需要立刻看什么):
- type 列(访问类型):
ALL:全表扫描 —— 这是最需要警惕的,90%的慢查询源于此。index:全索引扫描。range:索引范围扫描(较好)。ref/eq_ref/const:走索引且高效。
- rows 列: 预估扫描的行数,这个数字如果比实际返回的数据量大几个数量级(如扫描100万行返回10行),说明索引过滤性差。
- Extra 列(额外信息):
Using filesort:文件排序,说明查询中有ORDER BY但未利用索引,需要专门排序。Using temporary:用了临时表,常见于GROUP BY、DISTINCT或子查询。Using index(好):覆盖索引,回表少,性能好。Using where(中性):需要回表检查过滤条件。
示例(MySQL):
-- 假设慢查询: SELECT * FROM orders WHERE status = 'PENDING' ORDER BY create_time DESC; -- EXPLAIN 可能看到: -- type: ALL (全表扫描) -- rows: 5000000 -- Extra: Using filesort (还需要排序) -- 需要建立 (status, create_time) 的联合索引。
第三阶段:常见原因与快速对策
根据 EXPLAIN 结果,可以快速判断并给出解决方案:
| 现象(EXPLAIN中看到) | 常见原因 | 快速对策 |
|---|---|---|
| type=ALL, rows巨大 | 无索引或索引失效 | 立即创建索引(注意:大表加索引需谨慎,可能锁表,建议 pt-online-schema-change 或低峰期操作)。 |
| Extra=Using filesort | ORDER BY 字段无索引 |
在排序字段上建立索引,或将其包含在联合索引的最后。 |
| Extra=Using temporary | GROUP BY 或 DISTINCT 导致临时表 |
优化查询逻辑,或为 GROUP BY 字段建立索引。 |
| rows 大,但输出行很少 | 索引过滤性差(不是好索引) | 重建索引(如复合索引字段顺序不当导致只使用了前缀字段)。 |
| 锁等待 | SHOW OPEN TABLES WHERE In_use > 0 或查看 information_schema.INNODB_TRX |
检查是否有长事务未提交。 |
| 磁盘IO高 | 全表扫描或大量数据排序刷盘 | 先加索引,如果不行,考虑硬件升级(换SSD)。 |
第四阶段:高压下的“救命”操作
如果数据库已经卡死,无法正常登录或执行 EXPLAIN:
- 紧急止血(杀死慢查询线程):
- MySQL:执行
SHOW PROCESSLIST;找到Time很长的线程,用KILL [线程ID];强制终止。 - PostgreSQL:
SELECT pg_terminate_backend([pid]);
- MySQL:执行
- 临时降级:
- 关掉耗资源的非核心查询(如报表、后台统计任务)。
- 限流: 在应用层(如 Nginx、API 网关)对慢接口进行限流,防止雪崩。
快速排查 SOP(标准操作流程)
- 不能等: 立刻用
SHOW FULL PROCESSLIST或top找到 最耗时 的 SQL 或 CPU/IO 最高 的进程。 - 不猜: 拿到 SQL 后,立刻执行
EXPLAIN。 - 猛打: 如果看到
type=ALL,立刻评估加联合索引(ADD INDEX ...)。 - 后扫: 如果索引没问题,检查锁(
SHOW ENGINE INNODB STATUS)、检查数据量(是否真的大到需要分库分表)、检查服务器资源(内存、磁盘)。
最核心的一句话: 80%的慢查询问题,根源都是“没有索引”或“索引失效”。 先抓 EXPLAIN 看 type 和 rows。