EXPLAIN 关键列总览
理解 EXPLAIN 的输出要按列读:id 标识查询块,select_type 说明该行来自哪种子查询,type 是单表访问类型,key 是实际选中的索引,key_len 反映联合索引用到了前几列,rows 是预估扫描行数,filtered 是过滤后剩余比例,Extra 则给出排序、临时表等补充信息。把这些列串起来看,才能判断一条慢 SQL 卡在哪一步。只看 single 列容易误判,例如 possible_keys 有值但 key 为 NULL 表示索引没被选中。
基本信息
| id | 查询块编号,同一 id 的行属于同一个 SELECT 块 |
|---|---|
| select_type | 该行来源类型:SIMPLE、PRIMARY、SUBQUERY、DERIVED、UNION 等 |
| type | 单表访问类型:const、eq_ref、ref、range、index、ALL |
| key | 实际使用的索引,NULL 表示未走索引 |
| key_len | 索引查找用到的字节数,可推断命中前几列 |
| rows | 预估扫描行数,不是返回行数 |
| filtered | 条件过滤后剩余行的百分比(估算) |
| Extra | 补充信息:Using index、Using where、Using filesort 等 |
典型使用场景
- 慢查询定位:从 type 与 key 判断是否走了预期索引
- 联合索引验证:用 key_len 推算命中了前几列
- 连接顺序确认:结合 id 与 rows 看驱动表是谁
- 排序开销评估:看 Extra 里有没有 Using filesort
- 覆盖索引检查:Using index 表示无需回表
- 写语句预检:EXPLAIN 一条 UPDATE 看扫描量
示例
示例
EXPLAIN SELECT * FROM orders WHERE user_id = 100;
-- 输出列较多时,用 TREE 格式看单行摘要
EXPLAIN FORMAT=TREE SELECT * FROM orders WHERE user_id = 100;
-- JSON 格式便于程序化解析
EXPLAIN FORMAT=JSON SELECT * FROM orders WHERE user_id = 100;
-- 结合扫描行数与过滤比例判断是否需要补索引
EXPLAIN SELECT * FROM orders WHERE user_id = 100 AND status = 1;重点列解读
| 列 | 正常表现 | 异常信号 |
|---|---|---|
| type | ref 或 range | ALL 或 index 表示大范围扫描 |
| key | 命中预期索引 | NULL 表示未使用索引 |
| key_len | 与索引前缀长度相符 | 明显偏短说明只用到了前几列 |
| rows | 数量级较小 | 数量级偏大说明过滤不足 |
| filtered | 接近 100 | 很低说明大量行被读取后丢弃 |
| Extra | Using index 属好信号 | Using filesort、Using temporary 需关注 |
| select_type | SIMPLE 或 PRIMARY | DEPENDENT SUBQUERY 通常代价较高 |
possible_keys 只是候选清单,key 才是实际选择;两者不一致说明优化器放弃了那些索引,通常因为选择性不足或条件写法不合适。
rows 是估算值,偏差大时先更新统计信息(MySQL 用 ANALYZE TABLE,PostgreSQL 用 ANALYZE),再重新评估计划。
filtered 需要与 rows 一起看:两表连接时,rows 乘 filtered 才是传给下一步的估算行数,只盯一个数容易判断失误。
读列顺序建议
- 先看是否有全表扫描:type 列整体扫一遍
- 再看索引命中情况:key 与 key_len 决定实际用了哪棵索引的多少列
- 然后看行数估算:rows 与 filtered 一起判断过滤是否有效
- 最后看额外开销:Extra 里是否有文件排序或临时表
- 结合 SQL 语义复核:计划合理但结果不对时,多半是写法问题