如何读懂 MySQL 执行计划,重点该看哪些字段?
读懂MySQL执行计划应依次关注id与select_type判断执行顺序、type判断访问类型、possible_keys/key/key_len/ref确认索引使用,并结合rows与成本判断慢SQL是未走索引、过滤差还是排序或临时表导致;
学完这篇你会看清 EXPLAIN 结果里哪些字段是“必须看的”,以及每种取值背后代表什么样的执行代价,从而判断一条慢 SQL 到底是没走索引、索引走了但过滤差,还是排序、临时表拖了后腿。
第一步:先拿到执行计划,别急着下结论
这一步要做的,是把 SQL 交给优化器,让它输出执行计划;做完你手上会有一张或几张字段表。在 MySQL 客户端里,直接在你平时跑 SQL 的地方执行下面的语句:
EXPLAIN SELECT * FROM article WHERE category_id = 3 ORDER BY created_at DESC LIMIT 20;
MySQL 8.0 及以上还支持 EXPLAIN ANALYZE SELECT ...,它会真正执行语句并给出实际耗时与实际行数,比纯估算更准确。EXPLAIN FORMAT=JSON SELECT ... 则能拿到更细的 cost 信息。
注意:
EXPLAIN本身不执行查询(EXPLAIN ANALYZE会真的执行),所以对写操作或大表做分析时先用EXPLAIN,别一上来就用ANALYZE。
第二步:看 id 和 select_type,理清执行顺序
这一步是搞清楚这条 SQL 被拆成了几块、谁先谁后。id 越大越先执行,id 相同的从上往下执行;select_type 常见取值是 SIMPLE(无子查询或 UNION)、PRIMARY(最外层)、SUBQUERY(子查询里的第一个 SELECT)、DERIVED(派生表,即 FROM 里的子查询)、UNION。
看到 DERIVED 就要警惕:派生表通常会被物化成临时表,数据量大时开销明显。
第三步:看 type,这是最该先扫一眼的字段
type 表示访问类型,直接反映索引使用的好坏。从好到坏大致是:
system > const > eq_ref > ref > range > index > ALL
const/eq_ref:主键或唯一索引等值命中,通常只返回一行,最理想。ref:普通索引等值匹配,常见且可接受。range:索引范围扫描,BETWEEN、>、IN常见,一般可以接受。index:扫整个索引树,虽然用了索引但等于全扫,数据量大时很慢。ALL:全表扫描,最常见的性能杀手。
注意:
type为ALL不一定就是错的。小表、或者 WHERE 条件本身就要取大部分行时,全表扫描反而更快。要结合rows判断,而不是看到ALL就加索引。
第四步:看 key、key_len、possible_keys、ref,确认索引到底用了没
这一步回答“优化器本来能选哪些索引、实际选了哪个、用了几列”。possible_keys 是候选索引,key 是最终选中的索引(为 NULL 就是没走索引),ref 显示和索引做比较的列或常量,key_len 表示实际使用的索引字节数。
key_len 是判断联合索引“用了几列”的关键:联合索引 (a, b, c),如果 key_len 只等于 a 那一列的长度,说明 b、c 没被用上,多半是中间断了(比如 b 用了范围查询,或 c 上做了函数运算)。
注意:
key为NULL而possible_keys有值,说明优化器算过成本后主动放弃了索引,常见于条件区分度太低、或者回表成本高于全表扫描。这时该考虑的是覆盖索引,而不是硬加FORCE INDEX。
第五步:看 rows 和 filtered,估算真实数据量
rows 是优化器估算的需要扫描的行数,filtered 是这些行经过 WHERE 条件过滤后剩下的百分比。两者相乘,大致就是参与后续步骤的行数。
判断标准很直接:rows 越小越好;rows 很大而 filtered 很低(比如 5%),说明索引只帮你缩小了一部分范围,剩下的仍要逐行判断,这类查询往往是“索引建了但不够精准”。MySQL 8.0 的统计信息由 InnoDB 采样生成,不准时可以执行 ANALYZE TABLE 表名; 刷新。
第六步:看 Extra,问题基本都写在这
Extra 里的提示直接点名了性能隐患,重点看这几个:
Using index:覆盖索引,不用回表,是好信号。Using where:服务层还要再过滤,配合高rows就要警惕。Using index condition:索引下推(ICP)生效,通常比纯Using where好。Using filesort:需要额外排序,没利用上索引顺序。ORDER BY 的列和索引顺序不一致时必现。Using temporary:用了临时表,常见于 GROUP BY、DISTINCT、UNION,数据量大时很慢。
注意:
Using filesort不等于用了磁盘文件,内存里排序也叫这个名字;但它确实意味着多一次排序开销,能靠调整索引顺序消掉就消掉。
小结
- 先看
type,ALL和index是最该优化的两种访问类型,但要结合rows判断,小表全扫不算问题。 - 再看
key和key_len,key为NULL说明没走索引;key_len判断联合索引实际用到了第几列。 rows × filtered估算真实参与行数,两个数一起看才有意义。Extra里抓Using filesort、Using temporary、Using where这三个信号,Using index是好消息。- 需要真实数据时用
EXPLAIN ANALYZE(MySQL 8.0+),而不是只靠估算值下判断。
转载请注明出处,版权归原作者所有。