⑤ 透彻研究 EXPLAIN 输出的 SQL 执行计划

对应第 100 ~ 105 讲 · 12 个字段逐个击破 · select_type 与 type 全部取值

一行执行计划的 12 个字段(★ = 调优高频重点) select_type 取值一览 —— 每个 SELECT 扮演什么角色 rows × filtered —— 估算每步真实产出的数据量 id 与执行计划行数的对应关系 type 取值性能阶梯 —— 从快到慢(top → bottom) 拆出 12 个字段 按 SELECT 拆行 重点字段展开 多行怎么来的 其中 type 是重中之重 EXPLAIN + 你的 SQL explain select * from t1 where ... 执行计划表(12 个字段 × N 行) 简单 SQL 一行 · 复杂 SQL 按步骤拆成多行 逐行读懂执行计划 每一步怎么执行一目了然 id 每个 SELECT 对应一个编号 select_type 查询类型(见右侧取值一览) table 表名 / <union1,2> / <derived2> partitions 分区表概念 · 暂不关注 type ★ 访问方式(见右下性能阶梯) possible_keys ★ 有哪些索引可供选择 key ★ 实际选用的索引(成本决定) key_len 所用索引最大值的字节长度 ref ★ const=常量 · 库.表.字段=跨表匹配 rows ★ 预估扫描 / 读取的行数 filtered 其余条件过滤后剩余百分比 Extra ★ 附加执行信息(见⑥ 专题) SIMPLE 简单查询(单表/连接) PRIMARY 最外层的主查询 SUBQUERY 子查询里的 SELECT DEPENDENT SUBQUERY 依赖外层的相关子查询 UNION UNION 中后续的 SELECT DEPENDENT UNION 子查询内 UNION 的部分 UNION RESULT 临时表合并去重 <union1,2> DERIVED FROM 子查询物化 <derived2> 复杂 SQL 示例:子查询里套 UNION id=1 PRIMARY(外层) → id=2 DEPENDENT SUBQUERY → id=3 DEPENDENT UNION → UNION RESULT(<union2,3> 去重) DERIVED 示例:FROM (SELECT x1, count(*) ... GROUP BY x1) 别名 rows:这一步预估会查出来的行数 例:x1 > 'xxx' 走 range 索引 → 预估查出 1987 条 filtered:再用其余条件过滤后剩余的百分比 例:x2 = 'xxx'(无索引)过滤后剩 13% → 1987 × 13% ≈ 258 条 简单单表查询 → 只有 1 行执行计划 · id = 1 多表关联 → 每个表一行,id 相同(属于同一个 SELECT) 子查询 / UNION → 出现新的 id(2、3…)· 配合 select_type 阅读 ① const —— 主键 / 唯一索引等值 · 最快 ② eq_ref —— 被驱动表按主键做等值关联 ③ ref —— 二级索引等值匹配 ④ ref_or_null —— 等值匹配 + IS NULL ⑤ index_merge —— 多索引查完合并(交集/并集) ⑥ range —— 基于二级索引的范围查询 ⑦ index —— 扫描二级索引全部叶子节点 ⑧ all —— 全表扫描聚簇索引 · 必须重点消灭 连接查询专属:eq_ref · 单表特有:index_merge · 详见专题① 图例 普通字段 调优高频字段 ★ select_type 取值 type 快 type 慢

id 与 select_type 联动速记

  • • 一个 SELECT 子句对应一个 id
  • • 一个 SELECT 涉及多表 → 多行执行计划共用同一 id
  • • 出现子查询 / UNION → 出现新的 id 与新的 select_type
  • • UNION RESULT 的 table 是 <union1,2>:临时表去重
  • • DERIVED 的 table 是 <derived2>:FROM 子查询物化的临时表

possible_keys 有值但 key 为 NULL?

  • • 可能用到的索引 ≠ 实际使用的索引
  • • 优化器按成本估算后可能放弃索引
  • • 典型场景:x3='xxx' 几乎匹配全表 → 走索引 ≈ 全表扫描
  • • 还不如直接 ALL 全表扫,省去回表开销
  • • 这就是③ 成本优化的实际体现

ref 字段告诉你"拿什么去匹配"

  • • ref = const:拿一个常量值与索引列等值匹配
  • • ref = test_db.t1.id:拿另一个表的字段值匹配(连接查询)
  • • key_len:索引里值的最大字节长度,判断字段类型的线索
  • • rows × filtered ≈ 这一步真正产出的行数