RSS
菜单
全部文章快讯开发科技深度热点

第 13 章 EXPLAIN执行计划面试精讲

内容摘要

EXPLAIN 是 SQL 优化的"显微镜",也是面试中考察实战能力的核心。本篇基于 MySQL 8.0,逐列讲解 EXPLAIN 输出(id、select_type、table、type、possible_keys、key、key_len、rows、filtered、Extra),重点剖析 type 列的 system/const/ref/range/index/ALL 性能等级、Extra 字段常见值(Using index、Using where、Using temporary、Using filesort、Using join buffer)的含义与优化方向。学完后你能拿到一条慢 SQL 就知道"走没走索引、扫了多少行、要不要 filesort",并能讲清楚 EXPLAIN ANALYZE 的真实执行成本。

📖 本篇简介:EXPLAIN 是 SQL 优化的"显微镜",也是面试中考察实战能力的核心。本篇基于 MySQL 8.0,逐列讲解 EXPLAIN 输出(id、select_type、table、type、possible_keys、key、key_len、rows、filtered、Extra),重点剖析 type 列的 system/const/ref/range/index/ALL 性能等级、Extra 字段常见值(Using index、Using where、Using temporary、Using filesort、Using join buffer)的含义与优化方向。学完后你能拿到一条慢 SQL 就知道"走没走索引、扫了多少行、要不要 filesort",并能讲清楚 EXPLAIN ANALYZE 的真实执行成本。

目录

  • 题目 1:EXPLAIN 能告诉你什么,什么时候用它
  • 题目 2:EXPLAIN 各列含义,重点看哪几列
  • 题目 3:type 列有哪些取值,性能排序如何
  • 题目 4:const 与 system 的区别,什么时候出现
  • 题目 5:ref 与 eq_ref 的区别
  • 题目 6:range 索引扫描的常见场景
  • 题目 7:index 与 ALL 的区别,如何避免全表扫描
  • 题目 8:key_len 怎么算,有什么用
  • 题目 9:rows 与 filtered 列的含义,准确度如何
  • 题目 10:Extra 列 Using index 是什么意思
  • 题目 11:Using where 一定不好吗
  • 题目 12:Using temporary 是什么,怎么优化
  • 题目 13:Using filesort 是什么,怎么优化
  • 题目 14:Using join buffer 是什么意思
  • 题目 15:possible_keys 为 NULL 但 key 有值是怎么回事
  • 题目 16:key 列显示的索引不是期望的,为什么
  • 题目 17:id 列相同与不同代表什么
  • 题目 18:select_type 有哪些常见值
  • 题目 19:EXPLAIN ANALYZE 与传统 EXPLAIN 区别
  • 题目 20:常见坑与注意事项
  • 速查表:EXPLAIN 列速记
  • 复习清单
  • 练习题

题目 1:EXPLAIN 能告诉你什么,什么时候用它

考点

  • 执行计划的作用
  • 不实际执行 SQL(ANALYZE 除外)
  • 优化 SQL 的第一步

参考答案

1. EXPLAIN:输出 MySQL 优化器为这条 SQL 选择的执行计划,包括访问哪些表、用哪个索引、扫描多少行、是否排序/临时表。

2. 特点:普通 EXPLAIN 不真正执行 SQL,只输出"估计"的计划;EXPLAIN ANALYZE(8.0.18+)会真正执行并输出真实耗时与行数。

3. 什么时候用:

  • SQL 上线前评审;
  • 慢查询日志抓出来的 SQL;
  • 业务突然变慢时排查。

4. 基本用法:

-- 本示例基于 MySQL 8.0
EXPLAIN SELECT * FROM emp WHERE dept = '研发';
EXPLAIN FORMAT=JSON SELECT ...;   -- 更详细的 JSON 格式
EXPLAIN ANALYZE SELECT ...;       -- 8.0.18+,真实执行并输出耗时

追问

  • 追问:EXPLAIN 的 rows 是精确值吗?应对:是优化器基于索引统计的估算值,与真实值可能差几倍,要结合 EXPLAIN ANALYZE 看真实行。
  • 追问:EXPLAIN 会锁表吗?应对:普通 EXPLAIN 不会;EXPLAIN ANALYZE 会真正执行,对写 SQL 要小心。

题目 2:EXPLAIN 各列含义,重点看哪几列

考点

  • 12 列含义
  • 重点关注 type/key/rows/Extra
  • 输出格式

参考答案

| 列 | 含义 |

|----|------|

| id | 查询序号,越大越先执行 |

| select_type | 查询类型(SIMPLE/PRIMARY/SUBQUERY 等) |

| table | 访问的表名 |

| type | 访问类型(性能等级,重点看) |

| possible_keys | 可能用到的索引 |

| key | 实际用到的索引 |

| key_len | 使用的索引长度 |

| ref | 索引比较的来源 |

| rows | 预估扫描行数 |

| filtered | 过滤后的百分比 |

| Extra | 额外信息(重点看) |

重点关注:type(访问类型)、key(是否走索引)、rows(扫描行数)、Extra(是否临时表/排序)。

追问

  • 追问:FORMAT=JSON 比默认好在哪?应对:JSON 包含更详细的 cost_info、读取字节数、子查询信息,适合深度分析。
  • 追问:EXPLAIN 能看优化器选择理由吗?应对:8.0 可用 EXPLAIN FORMAT=TREE 或 OPTIMIZER_TRACE。

题目 3:type 列有哪些取值,性能排序如何

考点

  • 性能从好到差排序
  • 常见的几种类型
  • 生产至少要到 range

参考答案

从最好到最差:

| type | 含义 |

|------|------|

| system | 表只有一行(系统表) |

| const | 主键/唯一索引等值匹配,最多一行 |

| eq_ref | JOIN 时主键/唯一索引被引用,一行匹配 |

| ref | 普通索引等值匹配,可能多行 |

| range | 索引范围扫描(>、<、BETWEEN、IN) |

| index | 扫描整个索引树(比 ALL 好一点) |

| ALL | 全表扫描(最差,要优化) |

1. 生产标准:至少要到 range,最好是 ref 以上;出现 ALL 必须优化。

2. index vs ALL:index 是扫描整个索引(覆盖索引时可能比 ALL 快),ALL 是扫描整表数据。

3. 还有 ref_or_null(ref 后再查 NULL)、index_merge(多个索引合并)等少见值。

追问

  • 追问:index_merge 好吗?应对:一般,说明优化器没用好联合索引;能建联合索引代替就建。
  • 追问:type=index 一定比 ALL 快吗?应对:覆盖索引时 index 不用回表,可能比 ALL 快;非覆盖索引时 index 也要回表,不一定快。

题目 4:const 与 system 的区别,什么时候出现

考点

  • const:主键/唯一索引等值匹配
  • system:const 的特例,表只有一行
  • 性能最高

参考答案

1. system:表只有一行,是 const 的特例,基本只出现在系统表或衍生表。

2. const:通过主键或唯一索引做等值查询,最多匹配一行,优化器把它当常量。

-- emp 主键 id = 1,至多一行,type=const
EXPLAIN SELECT * FROM emp WHERE id = 1;

-- uk_name 是唯一索引
EXPLAIN SELECT * FROM emp WHERE name = '张三';

3. 要求:WHERE 条件必须是主键/唯一索引的所有列都做等值匹配。

4. 性能极好,一次定位,几微秒。

追问

  • 追问:唯一索引列中有 NULL 会怎样?应对:MySQL 唯一索引允许多个 NULL,等值查 NULL 走不到 const,走 ref。
  • 追问:联合唯一索引只匹配一部分列呢?应对:不算 const,算 ref 或 range。

题目 5:ref 与 eq_ref 的区别

考点

  • ref:单表普通索引等值
  • eq_ref:JOIN 时主键/唯一索引被引用
  • 性能差异

参考答案

1. ref:单表查询中普通索引(非唯一)做等值匹配,可能匹配多行。

2. eq_ref:JOIN 时,被驱动表用主键或唯一索引等值关联,对驱动表每一行,被驱动表只匹配一行。

3. eq_ref 是 JOIN 中最好的访问类型。

-- dept_id 普通索引,type=ref
EXPLAIN SELECT * FROM emp WHERE dept_id = 5;

-- JOIN:dept.id 是主键,emp.dept_id 关联 dept.id,dept 表 type=eq_ref
EXPLAIN SELECT e.name, d.name FROM emp e JOIN dept d ON e.dept_id = d.id;

追问

  • 追问:eq_ref 和 const 区别?应对:const 是单表主键等值(最多一行),eq_ref 是 JOIN 中被驱动表主键等值(对驱动表每行一行)。
  • 追问:JOIN 用普通索引关联会怎样?应对:被驱动表 type=ref,性能比 eq_ref 差。

题目 6:range 索引扫描的常见场景

考点

  • >、<、BETWEEN、IN、LIKE 前缀
  • 性能可接受
  • 必须走索引

参考答案

1. 常见触发条件:

  • WHERE age > 20
  • WHERE id BETWEEN 100 AND 200
  • WHERE dept_id IN (1,2,3)
  • WHERE name LIKE '张%'(前缀匹配)

2. 关键:列上必须有索引,否则 range 也走不到,直接 ALL。

3. IN 多了可能变 ALL:IN 列表太长,优化器觉得全表扫描更便宜,会放弃索引。

-- id 主键,type=range
EXPLAIN SELECT * FROM emp WHERE id BETWEEN 100 AND 200;
-- name 索引,前缀匹配,type=range
EXPLAIN SELECT * FROM emp WHERE name LIKE '张%';

追问

  • 追问:LIKE '%张' 走 range 吗?应对:不走,因为前缀模糊,索引失效,变 ALL。
  • 追问:IN (1,2,3,4,...,1000) 走索引吗?应对:数据量大时优化器可能放弃索引,要拆成多条或改写法。

题目 7:index 与 ALL 的区别,如何避免全表扫描

考点

  • index 扫索引树
  • ALL 扫数据行
  • 避免 ALL 的方法

参考答案

1. index:扫描整棵索引树,不回表(覆盖索引)时比 ALL 快;要回表时与 ALL 差不多。

2. ALL:全表扫描,逐行读取数据页,最差。

3. 避免 ALL:

  • WHERE 条件加合适索引;
  • 避免索引列上用函数/运算;
  • 避免前导模糊 LIKE '%xx';
  • 避免隐式类型转换。
-- 全表扫描:name 没索引或写法让索引失效
EXPLAIN SELECT * FROM emp WHERE LEFT(name,1)='张';  -- 函数包列,索引失效

-- 走索引
EXPLAIN SELECT * FROM emp WHERE name LIKE '张%';

追问

  • 追问:小表 ALL 比索引还快?应对:是,MySQL 优化器对小表(几百分页内)直接全表扫更便宜,走索引反而多一次回表。
  • 追问:怎么判断要不要建索引?应对:看 WHERE/JOIN/ORDER BY 中高频出现的列。

题目 8:key_len 怎么算,有什么用

考点

  • 验证联合索引用了几列
  • 长度计算规则
  • 字符集影响

参考答案

1. key_len:显示 MySQL 实际使用了索引的前多少字节,用来判断联合索引用到了第几列。

2. 计算规则:

  • 变长字段(VARCHAR):长度 × 字节数 + 2(变长头);
  • 定长字段(INT/DATE):固定字节数;
  • 允许 NULL 多 1 字节;
  • 字符集:utf8mb4 每字符 4 字节。

3. 举例:联合索引 idx(a VARCHAR(10), b INT),utf8mb4,a 允许 NULL:

  • 只用 a:key_len = 10×4 + 2 + 1 = 43
  • a + b:key_len = 43 + 4 = 47
-- 联合索引 idx_dept_job_sal(dept, job, salary)
-- 只查 dept='研发':key_len 显示 dept 部分的长度
EXPLAIN SELECT * FROM emp WHERE dept='研发';
-- 查 dept='研发' AND job='工程师':key_len 增大,说明用了前两列
EXPLAIN SELECT * FROM emp WHERE dept='研发' AND job='工程师';

追问

  • 追问:key_len 越小越好吗?应对:不是,key_len 大说明索引用得多,但也要看是否真的需要。
  • 追问:怎么知道索引是否完整用上?应对:对照 key_len 与索引定义列的理论长度。

题目 9:rows 与 filtered 列的含义,准确度如何

考点

  • rows 预估扫描行数
  • filtered 过滤百分比
  • 与真实值差距

参考答案

1. rows:优化器预估这次查询要扫描的行数,基于索引统计信息。

2. filtered:预估扫描后经过 WHERE 过滤剩下的行百分比(0~100)。

3. 实际产出行数 ≈ rows × filtered / 100。

4. 准确度:统计信息过期时 rows 与真实值可能差几倍到几十倍;ANALYZE TABLE 刷新统计信息后会准一些。

5. JOIN 中:多行 rows 相乘就是嵌套循环的总比较次数,是 JOIN 优化的关键指标。

追问

  • 追问:rows 大但实际很快为什么?应对:覆盖索引不回表、或者 LIMIT 提前终止,rows 是预估值不准。
  • 追问:怎么刷新统计信息?应对:ANALYZE TABLE tbl_name;。

题目 10:Extra 列 Using index 是什么意思

考点

  • 覆盖索引
  • 不回表
  • 性能好

参考答案

1. Using index:MySQL 只需要扫描索引就能拿到所有需要的列,不需要回表,即"覆盖索引"。

2. 好处:减少随机 I/O,性能好。

3. 触发条件:SELECT 的列都在索引中(包括 WHERE、JOIN、ORDER BY 用到的列)。

4. 示例:

-- 索引 idx_dept_sal(dept, salary)
-- SELECT 只要 dept, salary:覆盖索引,Using index
EXPLAIN SELECT dept, salary FROM emp WHERE dept='研发';

-- SELECT name:不在索引中,要回表,不会出现 Using index
EXPLAIN SELECT dept, salary, name FROM emp WHERE dept='研发';

追问

  • 追问:Using index condition 是什么?应对:8.0 索引下推 ICP,与 Using index 不同,是在索引层先过滤一部分条件再回表。
  • 追问:怎么设计覆盖索引?应对:把高频 SELECT 列都放进联合索引,但别贪多,索引有维护成本。

题目 11:Using where 一定不好吗

考点

  • Server 层过滤
  • 与索引条件下推区别
  • 正常出现

参考答案

1. Using where:存储引擎返回行后,Server 层再用 WHERE 条件过滤。

2. 不一定不好:如果 WHERE 条件能走索引,返回的行已经是过滤后的,Using where 只是额外兜底。

3. 不好的情况:type=ALL 且 Extra=Using where,说明全表扫描后还要 Server 层过滤,要加索引。

4. 与 ICP 区别:Using index condition 是 5.6+ 的索引下推,把过滤条件下推到存储引擎层,比 Using where 高效。

追问

  • 追问:Using where 和 Using index condition 哪个好?应对:后者更好,过滤下推到引擎层,减少回表次数。
  • 追问:什么情况下必然出现 Using where?应对:WHERE 条件无法完全走索引时。

题目 12:Using temporary 是什么,怎么优化

考点

  • 内部临时表
  • 常见于 GROUP BY/DISTINCT
  • 优化方向

参考答案

1. Using temporary:MySQL 为了完成查询建了内部临时表,常见于 GROUP BY、DISTINCT、UNION、子查询。

2. 内存/磁盘:临时表先在内存(tmp_table_size),超了落盘,性能骤降。

3. 优化方向:

  • 让 GROUP BY 列利用索引顺序,避免临时表;
  • 缩小结果集(提前 WHERE);
  • 适当调大 tmp_table_size。
-- GROUP BY 列与索引顺序不一致,容易出现 Using temporary
EXPLAIN SELECT dept, COUNT(*) FROM emp GROUP BY YEAR(hire_date);
-- hire_date 上加索引并改成直接按 hire_date 分组(应用层再聚合年)可优化

追问

  • 追问:临时表在内存还是磁盘怎么看?应对:状态变量 Created_tmp_tables(内存)vs Created_tmp_disk_tables(磁盘)。
  • 追问:临时表一定慢吗?应对:小数据量无所谓;大数据量落盘就慢。

题目 13:Using filesort 是什么,怎么优化

考点

  • 额外排序操作
  • 不一定是磁盘文件
  • 用索引消除

参考答案

1. Using filesort:MySQL 无法利用索引顺序完成 ORDER BY,需要额外排序(内存中 sort_buffer 或磁盘)。

2. 名字误导:filesort 不一定写磁盘,小结果集在内存排序。

3. 优化方向:

  • 让 ORDER BY 列与联合索引列顺序一致;
  • 排序方向与索引一致(8.0 支持降序索引);
  • 减小排序结果集(提前 WHERE + LIMIT)。
-- 索引 idx_dept_sal(dept, salary)
-- 下面这条 ORDER BY 能利用索引,不 filesort
EXPLAIN SELECT * FROM emp WHERE dept='研发' ORDER BY salary;

-- ORDER BY hire_date 与索引不一致,filesort
EXPLAIN SELECT * FROM emp WHERE dept='研发' ORDER BY hire_date;

追问

  • 追问:filesort 用什么内存?应对:sort_buffer_size,每个线程独享,别开太大。
  • 追问:LIMIT 10 能缓解吗?应对:能,排序只取前 10 行,用优先队列,比全排序快。

题目 14:Using join buffer 是什么意思

考点

  • BNL/BKA 算法
  • 被驱动表没索引
  • 加索引解决

参考答案

1. Using join buffer:JOIN 时被驱动表没索引,MySQL 把驱动表读到 join_buffer 里,再批量扫描被驱动表做匹配(Block Nested Loop)。

2. 性能差:说明 JOIN 缺少索引,要在被驱动表的关联列上建索引。

3. 优化:给被驱动表的 JOIN 列建索引,type 就会从 ALL 变成 ref/eq_ref。

-- emp.dept_id 没索引,JOIN dept 时 dept 端出现 Using join buffer
EXPLAIN SELECT e.name, d.name FROM emp e JOIN dept d ON e.dept_id = d.id;
-- 优化:CREATE INDEX idx_dept_id ON emp(dept_id);

追问

  • 追问:join_buffer_size 调大有用吗?应对:缓解但不能根治,根本是加索引。
  • 追问:BKA 是什么?应对:Batched Key Access,带索引的批量访问,比 BNL 好。

题目 15:possible_keys 为 NULL 但 key 有值是怎么回事

考点

  • 覆盖索引扫描
  • 优化器选了不在 possible_keys 中的索引
  • 常见于 COUNT(*)/MIN/MAX

参考答案

1. 原因:优化器发现某个索引(通常是覆盖索引)能直接完成查询,虽然它不在 WHERE 的候选索引中。

2. 典型场景:SELECT MIN(idx_col) FROM t,优化器直接扫索引最左页,possible_keys=NULL 但 key=idx_col。

3. 不是错误:说明优化器做了"聪明"的选择。

-- possible_keys=NULL, key=idx_hire_date
EXPLAIN SELECT MIN(hire_date) FROM emp;

追问

  • 追问:possible_keys=NULL 且 key=NULL 才是真没索引。应对:对,那才是要加索引的信号。
  • 追问:怎么看优化器为什么这么选?应对:用 OPTIMIZER_TRACE。

题目 16:key 列显示的索引不是期望的,为什么

考点

  • 优化器基于成本选择
  • 统计信息不准
  • 索引选错

参考答案

1. 原因:

  • 统计信息过期,优化器估算行数偏差;
  • 有多个可选索引,优化器选了它认为成本低的;
  • 索引列区分度低,优化器觉得走索引不如全表扫。

2. 解决:

  • ANALYZE TABLE 刷新统计信息;
  • 用 FORCE INDEX(idx_name) 强制指定索引(应急);
  • 删掉无用索引,减少优化器困惑。
-- 强制走指定索引
EXPLAIN SELECT * FROM emp FORCE INDEX(idx_dept) WHERE dept='研发';
-- 忽略某个索引
EXPLAIN SELECT * FROM emp IGNORE INDEX(idx_old) WHERE ...;

追问

  • 追问:FORCE INDEX 有什么风险?应对:数据分布变化后强制索引可能反而变慢,要定期评估。
  • 追问:怎么删多余索引?应对:sys.schema_unused_indexes 视图查长期未用的索引。

题目 17:id 列相同与不同代表什么

考点

  • id 越大越先执行
  • id 相同从上到下执行
  • 子查询/UNION 的执行顺序

参考答案

1. id 不同:id 越大越先执行(子查询先于外层)。

2. id 相同:从上到下顺序执行(JOIN 表的顺序)。

3. id 为 NULL:UNION 的临时表行,最后执行。

-- 子查询:id=2 的子查询先执行,id=1 的外层后执行
EXPLAIN SELECT * FROM emp WHERE dept_id IN (SELECT id FROM dept WHERE name='研发');

追问

  • 追问:JOIN 表顺序是优化器定的吗?应对:是,优化器基于成本选驱动表;STRAIGHT_JOIN 可强制顺序。
  • 追问:子查询优化器会改写成 JOIN 吗?应对:8.0 对半连接做了大量优化,IN 子查询常被改写成 JOIN。

题目 18:select_type 有哪些常见值

考点

  • SIMPLE/PRIMARY/SUBQUERY/DERIVED/UNION
  • 各值含义
  • 性能提示

参考答案

| select_type | 含义 |

|-------------|------|

| SIMPLE | 简单查询,无子查询/UNION |

| PRIMARY | 外层查询 |

| SUBQUERY | 子查询(非 FROM 中) |

| DERIVED | FROM 中的派生表 |

| UNION | UNION 中第二个及之后的查询 |

| UNION RESULT | UNION 结果临时表 |

| DEPENDENT SUBQUERY | 相关子查询(外层引用) |

1. DEPENDENT SUBQUERY 要警惕:相关子查询对外层每行都执行一次,性能差。

2. DERIVED 多了:派生表物化成本高,8.0 有条件下推优化。

追问

  • 追问:DEPENDENT SUBQUERY 怎么优化?应对:改写成 JOIN,让优化器一次扫描。
  • 追问:DERIVED 8.0 有什么优化?应对:8.0 支持 derived_condition_pushdown,把外层条件下推到派生表。

题目 19:EXPLAIN ANALYZE 与传统 EXPLAIN 区别

考点

  • 真实执行 vs 预估
  • 输出实际行数与耗时
  • 8.0.18+ 支持

参考答案

1. 传统 EXPLAIN:只输出优化器估计的计划,不真正执行。

2. EXPLAIN ANALYZE:8.0.18+,真正执行 SQL,输出每一步的实际行数、实际耗时、循环次数。

3. 优势:能对比"预估 rows"与"实际 rows",发现统计信息不准的问题。

4. 注意:会真正执行 SQL,对写操作或大查询要小心。

-- 输出每一步的 actual rows、time
EXPLAIN ANALYZE
SELECT e.name, d.name FROM emp e JOIN dept d ON e.dept_id = d.id;

追问

  • 追问:EXPLAIN ANALYZE 能用于 UPDATE 吗?应对:能,但会真正执行 UPDATE,生产环境慎用。
  • 追问:输出格式是什么?应对:树状文本,类似 -> Nested loop inner join (cost=...)(actual time=... rows=... loops=...)。

题目 20:常见坑与注意事项

考点

  • EXPLAIN 使用高频坑
  • 预估值与真实值差异
  • 版本差异

参考答案

1. 坑 1:把 rows 当真值,其实是估算,差异可能很大。

2. 坑 2:type=ALL 不一定立刻加索引,小表全表扫反而快。

3. 坑 3:filesort 名字吓人,小结果集在内存排,不慢。

4. 坑 4:Using temporary 不代表磁盘临时表,内存里也可能。

5. 坑 5:FORCE INDEX 应急用,别长期依赖。

6. 坑 6:EXPLAIN 在不同数据量下结论不同,要在生产数据量级下测。

7. 坑 7:统计信息过期导致走错索引,定期 ANALYZE TABLE。

8. 坑 8:EXPLAIN ANALYZE 会真正执行,写 SQL 生产慎用。

追问

  • 追问:怎么看索引统计信息是否过期?应对:SHOW TABLE STATUS 的 Update_time 与数据变化频率对比。
  • 追问:EXPLAIN 输出可以导出吗?应对:EXPLAIN FORMAT=JSON 输出 JSON,可脚本解析。

速查表:EXPLAIN 列速记

| type 值 | 性能 | 出现条件 |

|---------|------|----------|

| system | 极好 | 表只有一行 |

| const | 极好 | 主键/唯一索引等值 |

| eq_ref | 很好 | JOIN 被驱动表主键/唯一 |

| ref | 好 | 普通索引等值 |

| range | 可接受 | 索引范围扫描 |

| index | 较差 | 全索引扫描 |

| ALL | 差 | 全表扫描 |

| Extra 值 | 含义 | 是否要优化 |

|----------|------|-----------|

| Using index | 覆盖索引,不回表 | 否,好事 |

| Using where | Server 层过滤 | 视情况 |

| Using index condition | 索引下推 ICP | 否,好事 |

| Using temporary | 用了临时表 | 视数据量 |

| Using filesort | 额外排序 | 视情况 |

| Using join buffer | JOIN 无索引 | 是,加索引 |

| Start temporary / End temporary | 半连接去重临时表 | 注意 |

复习清单

| 题目 | 核心结论 | 易错点 |

|------|----------|--------|

| 1 | EXPLAIN 看执行计划,ANALYZE 真实执行 | rows 当真值 |

| 2 | 重点看 type/key/rows/Extra | 列太多不知道看啥 |

| 3 | type 从 const 到 ALL 性能递减 | ALL 就立刻加索引 |

| 4 | const 是主键/唯一等值 | 唯一索引多 NULL 不走 const |

| 5 | eq_ref 是 JOIN 最优访问类型 | ref 与 eq_ref 混淆 |

| 6 | range 是索引范围扫描 | LIKE '%xx' 不走 range |

| 7 | index 扫索引,ALL 扫数据 | 小表 ALL 可能更快 |

| 8 | key_len 判断联合索引用了几列 | 字符集字节数算错 |

| 9 | rows × filtered 是产出行数 | 统计信息过期不刷新 |

| 10 | Using index = 覆盖索引 | 与 Using index condition 混淆 |

| 11 | Using where 不一定差 | 看到就想优化 |

| 12 | Using temporary 常见于 GROUP BY | 临时表落盘不监控 |

| 13 | Using filesort 不一定磁盘 | 以为 filesort 必慢 |

| 14 | Using join buffer 说明缺 JOIN 索引 | 只调 join_buffer_size |

| 15 | possible_keys=NULL 但 key 有值是覆盖扫描 | 以为是错误 |

| 16 | 优化器选错索引用 FORCE INDEX 应急 | 长期依赖 FORCE |

| 17 | id 越大越先执行 | UNION 临时表 id 为 NULL |

| 18 | DEPENDENT SUBQUERY 要改 JOIN | 相关子查询性能差 |

| 19 | EXPLAIN ANALYZE 真实执行 | 生产环境乱用 |

| 20 | 统计信息、小表、FORCE 三大坑 | 不在生产数据量下测 |

练习题

1. 题目:EXPLAIN 输出 type=ALL、Extra=Using where,说明什么?怎么优化?

  • 参考答案:全表扫描 + Server 层过滤;要在 WHERE 列上建索引,让 type 升到 range/ref。

2. 题目:联合索引 idx(a,b,c),WHERE 只用 a=1 AND c=3,key_len 体现用了几列?

  • 参考答案:最左前缀原则下只用到 a 列,key_len 等于 a 列长度;b 没用到,c 用不上。

3. 题目:Extra=Using filesort 一定意味着磁盘排序慢吗?

  • 参考答案:不一定,小结果集在 sort_buffer 内存排序;大数据量才落盘。

4. 题目:怎么判断临时表是在内存还是磁盘?

  • 参考答案:对比状态变量 Created_tmp_tables 与 Created_tmp_disk_tables,后者大说明落盘多。

5. 题目:EXPLAIN ANALYZE 与 EXPLAIN 最大的区别是什么?

  • 参考答案:ANALYZE 真正执行 SQL,输出实际行数与耗时;普通 EXPLAIN 只输出优化器估计。

下一篇预告:第 14 章 SQL优化面试精讲

— 全文完 —回到顶部 ↑
下载推广海报

文章推广海报

《第 13 章 EXPLAIN执行计划面试精讲》完整推广海报
DISCUSSION

文章回复

0 条公开回复
未登录回复需要审核后公开
还没有回复,欢迎参与讨论。