前言
EXPLAIN 是排查慢 SQL 的第一现场。它只做解析不真正执行,所以在线上随手加也不会出事故。下面按列拆解,一条一条说清每列"能告诉你什么、不能告诉你什么"。
mysql> EXPLAIN SELECT id FROM blog_article WHERE category_id = 12
-> ORDER BY published_at DESC LIMIT 10;
+----+-------------+--------------+------+--------------+---------+------+-----------------------------------------+
| id | select_type | table | type | key | key_len | ref | rows | Extra |
+----+-------------+--------------+------+--------------+---------+------+-----------------------------------------+
| 1 | SIMPLE | blog_article | ref | idx_category | 4 | const| 1 | Using index condition; Using filesort |
+----+-------------+--------------+------+--------------+---------+------+-----------------------------------------+
5.7 完整列是:id、select_type、table、partitions、type、possible_keys、key、key_len、ref、rows、filtered、Extra。
type:访问方式,最关键的一列
从好到坏大致是这个顺序:
| type | 含义 |
|---|---|
| system / const | 主键或唯一索引等值,最多一行 |
| eq_ref | join 时按主键唯一索引匹配,每行外部表只回一行 |
| ref | 非唯一索引等值(或前缀)匹配,可能返回多行 |
| range | 索引范围扫描,BETWEEN、>、IN、右模糊 |
| index | 扫整棵索引树,比 ALL 省在不用读行数据 |
| ALL | 全表扫描,看到就要警惕 |
还有 index_merge、fulltext、ref_or_null、unique_subquery 等,属于特定场景。经验判断:等值查询出现 ALL 是缺索引,index 通常意味着覆盖索引全扫或者优化器宁可用索引顺序避免排序。
possible_keys 与 key
possible_keys 是候选,key 才是真正选中的。候选有、key 是 NULL,说明优化器算了一遍觉得不走更快——常见于表很小、或者命中比例太高等量级问题。反过来 key 有值不代表就快,还要接着看 rows 和 Extra。
key_len:索引实际用了几列
它是"用到索引的第几列"最直接的证据。计算规则:定长类型按字节数算(INT 是 4),变长类型是字符集字节乘长度再加 2 个长度字节,可空列再加 1。以 KEY (status, category, views) 为例,只用 status 时 key_len 是 1(TINYINT),用到 status 和 category 时是 1+4=5。最左前缀到底吃了几列,看这个数比猜 SQL 写法准。
rows 和 filtered 都是估算
rows 是优化器预计要检查的行数,来自索引统计信息(ANALYZE TABLE 会刷新),跟真实结果集往往有偏差,数据倾斜时更明显。filtered 是预计经过条件过滤后剩下的百分比,两者相乘是优化器估算 join 代价的输入。判断"要不要建索引"时把它们当量级参考,别当结论。
Extra:最信息密集的一列
Using index:覆盖索引,不用回表,最理想Using index condition:索引条件下推 ICP,在引擎层先过滤Using where: Server 层还要再过滤一遍Using filesort:需要额外排序,看下一篇Using temporary:需要临时表,多见于GROUP BY跟不上的场景Using join buffer:join 没走索引,退化成块嵌套循环Impossible WHERE、Select tables optimized away:分别是条件恒假、以及MAX()之类能被优化成直接读索引端点
两个补充用法
EXPLAIN FORMAT=JSON SELECT ...; -- 看 cost_info,能看到预计成本
EXPLAIN FOR CONNECTION <connection_id>; -- 5.7 支持,直接分析正在跑的线程
EXPLAIN 只显示计划,不显示实际执行时间。想确认优化器的行数估得准不准,改完索引再对比一次执行计划,比单看耗时可靠。
小结
type定性质,key定选择,key_len定用了几列,rows给量级,Extra给细节possible_keys有而key为空,是优化器主动放弃,不一定是索引建错- 估算列会骗人,重要结论要用改索引前后的对比来支撑