大厂都在用的SQL调优利器:一眼看穿慢SQL,性能飙升数10倍!
MySQL慢SQL诊断利器EXPLAIN详解:执行计划分析、优化策略与常见陷阱 摘要:EXPLAIN是MySQL内置的慢SQL诊断工具,通过模拟优化器执行逻辑输出SQL的执行计划。它能帮助开发者确认表的访问方式、索引使用情况、连接顺序、额外开销操作和预估扫描行数等关键信息。文章详细解析了执行计划的核心字段,包括id、type、possible_keys等指标的含义,并提供了基础使用方法与高阶技巧(
当你盯着数据库监控面板上飙升的查询耗时,或者用户反馈页面加载像蜗牛爬时,你有没有想过,到底是哪段 SQL 在拖后腿?其实,排查慢 SQL 不用瞎猜,MySQL 早给我们准备了一个慢 SQL 诊断利器 ——EXPLAIN。EXPLAIN 它就像医生的"X 光"拍片机一样,过一下就能帮你找出病灶在哪。可以说超过 95% 以上的慢 SQL 问题,都能通过 EXPLAIN 执行计划精准定位。所以作为后端开发或 DBA 一定要会用 MySQL 自带的这些工具。
一、EXPLAIN 到底能帮你看什么?
EXPLAIN 的核心作用是模拟 MySQL 优化器的执行逻辑,输出 SQL 的执行计划,说白了就是告诉你 MySQL 打算怎么执行这条 SQL:先查哪张表、用不用索引、扫多少行数据、会不会排序建临时表等。
所以 EXPLAIN 能帮你确认这几件事:
- 表的访问方式:是走索引还是全表扫描
- 索引使用情况:实际用到了哪个索引,有没有索引失效
- 表连接顺序:多表关联时优化器选择的执行顺序
- 额外开销操作:是否需要文件排序、临时表这些高成本步骤
- 预估扫描行数:优化器预判要扫描多少行数据
二、读懂 SQL 执行计划核心字段
先看下执行 EXPLAIN 返回的内容是什么(如下图)。EXPLAIN 输出的每一列都有其独特的含义,往往也是隐藏着性能的关键信息,我们拣最关键的几个指标看一下:
- id:表示查询的执行顺序,id 越大优先级越高,先执行子查询再执行外层查询;id 相同则从上到下依次执行。要是你看到 id 为 NULL 的行,那一般是 UNION 操作的临时结果表,最后才会处理。
- type:这是判断 SQL 性能的核心指标,它代表 MySQL 访问表的方式,从优到劣排序是:
system > const > eq_ref > ref > range > index > ALL。简单说,const是通过主键或唯一索引直接定位单行数据,性能最好;eq_ref常见于 JOIN 查询中用主键 / 唯一索引关联;ref是非唯一索引的等值匹配;range是范围查询(比如 BETWEEN、IN);而ALL就是全表扫描,这是我们要尽量避免的情况,除非你的表本身就没几条数据。 - possible_keys & key:
possible_keys是 MySQL 认为可能用到的索引列表,key是实际用到的索引。要是key为 NULL,说明没用到索引,这时候就得排查是不是索引失效了 —— 比如 WHERE 条件里用了函数、做了隐式类型转换,或者用了左模糊查询(LIKE '%abc'),这些情况都会让索引直接 “罢工”。 - rows:MySQL 优化器预估要扫描的行数,这个数值越接近实际返回行数越好。要是预估行数和真实行数差太多,说明 MySQL 的统计信息过时了,跑个
ANALYZE TABLE更新一下统计信息就行。 - Extra:这里是最直观的优化信号源。比如
Using filesort表示 MySQL 没法用索引排序,需要在内存或磁盘做额外排序,性能开销极大;Using temporary表示要创建临时表来存中间结果,常见于 GROUP BY 或 ORDER BY 的字段没建索引;Using index则是最优情况,说明用了覆盖索引,不用回表查数据,性能拉满;Using where则表示 MySQL 会在服务器层过滤数据,要是能把过滤逻辑下推到存储引擎层,性能还能再提一提。
三、EXPLAIN 的基础使用
它的用法其实很简单,就在你要分析的 SQL 前加 EXPLAIN 关键字就行,我们就用一个简单的语句举例来看,比如:
EXPLAIN SELECT * FROM users WHERE age = 25;
当然了,EXPLAIN 还有高阶用法,比如加上 ANALYZE 参数可以实际执行 SQL,得到真实的执行时间和扫描行数,对比预估数值能判断优化器的估算是否准确;加上 FORMAT=JSON 可以获取更详细的执行计划,包括成本估算、循环次数等细节。
如下是执行 EXPLAIN ANALYZE
EXPLAIN ANALYZE SELECT * FROM users WHERE age = 25;
返回内容如下:
-> Filter: (users.age = 25) (cost=51068.55 rows=49971) (actual time=0.721..1359.595 rows=10117 loops=1)
-> Table scan on users (cost=51068.55 rows=499708) (actual time=0.714..1270.600 rows=505000 loops=1)
不过要注意,**EXPLAIN ANALYZE 会实际执行 SQL**,如果是写操作,要放在事务里执行后回滚,避免影响业务数据。
四、从执行计划到优化落地
光看懂字段还不够,得知道怎么根据执行计划改 SQL。一般来说,既然找到了问题,优化方向就很明确了 —— 给查询条件和排序字段创建合适的索引。但是别急,先往下继续看。
比如你拿到一条慢 SQL,先跑 EXPLAIN,要是看到 type 是 ALL,key 是 NULL,那十有八九是没建合适的索引。这时候别急着加索引,先看 WHERE 条件里的字段,是不是符合最左前缀原则?比如你建了联合索引 idx_age_name(age, name),但 WHERE 条件只写了 name = '张三',那这个索引就用不上,因为联合索引得从最左列开始匹配。
再比如,要是 Extra 里出现 Using filesort,那得看看 ORDER BY 的字段有没有建索引。要是你的 SQL 是:
SELECT id, name FROM users ORDER BY create_time DESC;
那给 create_time 建个索引就能解决排序问题;但要是你还加了 WHERE 条件:
SELECT id, name FROM users WHERE age > 20 ORDER BY create_time DESC
因为 MySQL 通常一次查询只会用到一个索引,所以要让 SQL 能选到最合适最高效的索引才能事半功倍。那得建联合索引 idx_age_create_time(age, create_time),这样既能用索引过滤数据,又能利用索引的有序性避免排序。
还有一种常见情况,就是子查询导致的性能问题。比如
SELECT * FROM users WHERE id IN (SELECT user_id FROM orders WHERE status = 1);
有些版本的 MySQL 对子查询的优化不好,会先执行子查询再循环查主表,这时候你可以把子查询改写成 JOIN:
SELECT u.* FROM users u JOIN orders o ON u.id = o.user_id WHERE o.status = 1;
再看执行计划,type 可能直接从 ALL 变成 eq_ref,性能提升不止一个档次。
重要注意事项 :
在上面已经说过了,要是你要分析修改数据的 SQL(比如 UPDATE、DELETE),又怕影响线上数据,那可以把 SQL 放在事务里,加 EXPLAIN ANALYZE 执行,分析完再回滚事务,这样就能拿到真实的执行数据,又不会改动现有数据了。
四、容易踩到的慢 SQL 的坑
最后聊聊几个容易踩的坑,帮你避开不必要的性能问题:
- 前导通配符导致索引失效:比如
LIKE '%手机%'这种写法,会让索引完全失效,只能全表扫描,换成LIKE '手机%'或者使用全文索引才能解决。 - 隐式类型转换:如果索引字段是整数类型,SQL 里却传了字符串,比如
WHERE id = '123',MySQL 会自动做类型转换,导致索引失效,在见这种情况一定要统一数据类型。 - OR 条件的误区:很多人以为 OR 条件一定会导致索引失效,其实在 PostgreSQL 11 + 或者 MySQL 8.0 的某些版本中,优化器会自动用 BitmapOr 来处理,不过如果是低版本数据库,还是建议用 UNION 重写或者创建多列索引。
- 索引低区分度 :索引区分度很低时,MySQL 可能会觉得用索引还不如全表扫描快,直接就跳过索引了。比如你给
gender字段建索引,因为只有男、女两种值,区分度极低,大概率是不会走索引的。所以建索引前,最好先算一下字段的区分度:SELECT COUNT(DISTINCT column)/COUNT(*) AS selectivity FROM table;,一般区分度超过 20% 的字段建索引才有意义。 - 统计信息过期:如果表数据有大量新增或删除,优化器的统计信息会不准确,导致预估行数和实际相差巨大,这时候要记得执行
ANALYZE TABLE更新统计信息。
重点要说的是 MySQL 的统计信息过期的问题,有时候你会发现,明明建了索引,执行计划却还是走全表扫描,这时候你得看看 MySQL 的统计信息准不准确。比如你有个表刚导入了 100 万条数据,但 MySQL 的统计信息还是旧的,它会误以为表的数据很少,就选择了全表扫描。这时候跑个 ANALYZE TABLE 表名; 更新统计信息,再看执行计划,大概率就会用索引了。
其实,EXPLAIN 的核心价值就是帮你跳出 “凭感觉优化” 的误区,用数据说话。只要你能读懂执行计划里的每个字段,大部分慢查询问题都能迎刃而解。下次遇到 SQL 慢的情况,先别急着加索引或者改 SQL,跑一遍 EXPLAIN 看看,大概率能帮你找到 SQL 慢的原因。我们日常中几乎都是用这个工具来帮我们解决慢 SQL 的问题。整个解决慢 SQL 的过程大概如下图所示,经历抓取慢 SQL->EXPLAIN 分析慢 SQL-> 索引和 SQL 优化-> 验证的过程,有时可能要经历多轮反复分析修改验证的过程。
更多推荐


所有评论(0)