订阅更新

一条查询突然变慢了,你怎么排查?

MySQL手册 第 1 题 约 5 分钟读完

本文目录
  1. 先说结论
  2. 业务场景:订单查询突然变慢
  3. 第一步:找到慢SQL
  4. 第二步:EXPLAIN分析执行计划
  5. 第三步:定位原因并优化
  6. 面试高频追问:常见的索引失效场景
  7. 实战踩坑:索引加了但没用上
  8. 总结

先说结论

慢查询排查四步走:

① 开启慢查询日志,找到慢SQL —— 先知道“谁慢了”

② 用EXPLAIN分析执行计划 —— 看“为什么慢”

③ 根据分析结果优化 —— 加索引、改SQL、调配置

④ 验证优化效果 —— 确认“真的快了”

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

慢查询排查是MySQL面试的“开局王炸”,几乎每场必问。但很多人的回答停留在“看EXPLAIN”这一步就卡住了——EXPLAIN看什么、怎么看、看完怎么优化,说不清楚。今天用一个真实的生产场景,把慢查询排查的完整流程走一遍。

业务场景:订单查询突然变慢

我在元宇宙社交App那家公司,有个订单查询接口,原本响应时间20ms,突然变成了3秒。服务端开始报超时告警。

-- 问题SQL
SELECT o.order_id, o.amount, u.nickname
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.status = 'paid'
  AND o.created_at > '2025-01-01'
ORDER BY o.created_at DESC
LIMIT 20;

第一步:找到慢SQL

-- 开启慢查询日志
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1;  -- 超过1秒记录
 
-- 查看慢查询日志位置
SHOW VARIABLES LIKE 'slow_query_log_file';

生产环境我们一般用监控工具(如Percona PMM或阿里云RDS的慢SQL分析)直接查看,不用手动开日志。

第二步:EXPLAIN分析执行计划

EXPLAIN SELECT o.order_id, o.amount, u.nickname
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.status = 'paid'
  AND o.created_at > '2025-01-01'
ORDER BY o.created_at DESC
LIMIT 20;

EXPLAIN结果里最关键的几个字段:

字段含义你应该看什么
type访问类型ALL=全表扫描(危险),ref/range=用到索引(正常)
key实际使用的索引NULL=没用索引(危险)
rows预估扫描行数越小越好
Extra额外信息Using filesort/Using temporary=可能有问题

我看到的结果是:orders表的type是ALL,key是NULL,rows显示500万。意思是全表扫描了500万行,难怪慢。

第三步:定位原因并优化

问题很清楚:status和created_at上没有合适的索引。解决方案:加一个组合索引。

-- 加组合索引(核心优化)
ALTER TABLE orders ADD INDEX idx_status_created (status, created_at);
 
-- 再次EXPLAIN,type变成range,rows变成2000,查询时间从3秒→ 15ms

为什么是(status, created_at)而不是单独的索引?因为WHERE里同时用到了这两个字段,组合索引可以一次性过滤,效率更高。而且status放前面是因为它是等值条件(=),created_at是范围条件(>),等值条件在前才能充分利用索引。

面试高频追问:常见的索引失效场景

这才是面试官真正想听的。我总结了生产环境最常见的5种索引失效场景:

① 对索引列做函数/运算:

-- ✖ 索引失效:对created_at做了函数转换
WHERE DATE(created_at) = '2025-03-15'
 
-- ✔ 索引生效:用范围查询
WHERE created_at >= '2025-03-15' AND created_at < '2025-03-16'

② 隐式类型转换: 字段是varchar但查询用了数字,MySQL会自动转换,索引失效。

-- ✖ phone是varchar,但用了数字查询,触发隐式转换
WHERE phone = 13800138000
 
-- ✔ 加引号,避免隐式转换
WHERE phone = '13800138000'

③ 最左前缀原则违反: 组合索引(a,b,c),查询只用了b和c,跳过了a,索引失效。

④ LIKE以%开头: LIKE '%keyword'无法使用索引,LIKE 'keyword%'可以。

⑤ OR条件: 如果OR的两侧不是都有索引,MySQL可能放弃索引走全表扫描。

实战踩坑:索引加了但没用上

我在元宇宙社交App还遇到过一个更难发现的问题:明明加了索引,EXPLAIN也显示用了索引,但查询还是慢。原因是返回的数据量太大——索引过滤后仍有50万行,还要回sort。解决方案是加了更精确的过滤条件(缩小时间范围),并用覆盖索引避免回表。

-- 覆盖索引:索引包含了查询需要的所有字段,不需要回表
ALTER TABLE orders ADD INDEX idx_cover (status, created_at, order_id, amount);
-- EXPLAIN显示Using index,说明不需要回表了

总结

慢查询排查四步走:找到慢SQL→EXPLAIN分析→加索引/改SQL→验证效果。EXPLAIN重点看type、key、rows、Extra。熟记5种索引失效场景。面试时用一个真实的排查案例串起来说,比干巴巴列规则有说服力得多。

下一篇聊一个很具体的场景题:一张订单表5000万行,查询越来越慢,你怎么办?分库分表、归档、还是其他方案?

你们排查慢查询的流程是什么?评论区聊聊~

关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长

刷题进度保存在当前浏览器