Files
autumn-recruitment/02.MySQL/index/Explain 执行计划解读_test.md
T

10 KiB
Raw Blame History

tags, create time
tags create time
test/review
mysql
explain
execution-plan
filesort
2026-08-09 12:00

Explain 执行计划解读_测试题

概述

本测试覆盖 MySQL EXPLAIN 执行计划的全部关键字段,包括 type 访问类型、select_type 查询类型、possible_keys/key/key_len 索引选择、rows/filtered 行数估算,以及 Extra 常见值。共 10 道题:6 道选择题、3 道填空题、1 道简答题。


一、选择题(6道,由浅入深)

难度阶梯: Q1-Q2 基础概念 → Q3-Q4 核心原理 → Q5-Q6 深入应用/边界场景

Q1(基础)— 考察定义层面

EXPLAIN 命令在 SQL 真正执行之前做什么?

A. 直接执行 SQL 并记录运行时间 B. 模拟优化器生成 SQL 的执行计划 C. 检查 SQL 语法是否正确 D. 锁定相关表的元数据防止并发修改

Q2(基础)→ 考察行为判断

以下 EXPLAIN 输出中,type 值代表查询质量最好的是哪一项?

A. range B. ALL C. const D. ref

Q3(进阶)→ 考察原理理解

为什么 index 类型的查询性能优于 ALL 类型?

A. index 扫描的是索引树,通常为有序排列,可利用顺序 IO;ALL 扫描的是数据行,属于随机 IO B. index 是 MySQL 8.0 新增的高级扫描方式,底层使用哈希加速 C. ALL 类型只在 InnoDB 中出现,MyISAM 没有这个类型 D. index 会自动启用索引下推,而 ALL 不能

Q4(进阶)→ 考察比较辨析

关于 EXPLAIN 的 possible_keys 和 key 字段,以下说法正确的是:

A. possible_keys 是实际使用的索引,key 是候选索引列表 B. 两者含义相同,只是别名关系 C. possible_keys 是优化器认为可能用到的候选索引,key 是最终选择的索引 D. 如果 key 为 NULL 但 possible_keys 不为 NULL,说明优化器选错了索引

Q5(深入)→ 考察场景推理

执行以下 SQL 的 EXPLAIN 时,Extra 列会出现什么警告信息?

EXPLAIN SELECT * FROM orders
WHERE YEAR(created_at) = 2025;

A. Using index condition B. Using temporary; Using filesort C. Using where; Using filesort D. Impossible where

Q6(深入)→ 考察源码级细节

在 EXPLAIN 输出中,rows 和 filtered 的含义分别是:

A. rows 是表中总行数,filtered 是实际返回的行数 B. rows 是优化器估算的需要扫描的行数,filtered 是按条件筛选后的留存行百分比 C. rows 是实际执行的扫描行数,filtered 是被索引过滤掉的比例 D. rows 是 Join Buffer 的大小,filtered 是缓存命中率


二、填空题(3道)

F1 — type 排序

MySQL 的 type 访问类型按性能从高到低排列为:

system > const > ______ > ref > range > ______ > ALL

请填入中间两个缺失的类型名称。

提示: 回想原文中的性能金字塔——eq_ref 是等值引用(前一个表的每一行通过唯一索引精确匹配一行),index 是全索引扫描。

F2 — key_len 字节计算

有一张表:

CREATE TABLE users (
    name VARCHAR(64) NOT NULL,
    age INT NOT NULL,
    INDEX idx_name_age(name, age)
);

字符集 utf8mb4(每字符 4 字节)。当执行以下查询时:

EXPLAIN SELECT * FROM users WHERE name = 'Alice' AND age = 25;

key_len 的预期值为 262 字节,其中:

  • name: 64 × 4 = 256 + ______ 字节变长标记 + ______ 字节 NULL 标记 = 258 字节
  • age: INT 占 ______ 字节(NOT NULL,无 NULL 标记)

提示: 变长字段无论是否 NOT NULL 都需要 1 字节标记实际长度;NULL 标记只在字段允许 NULL 时才存在。

F3 — Extra 字段诊断

以下为几条 SQL 的 Extra 诊断填空:

SQL 特征 Extra 值 含义
SELECT id, name FROM users WHERE name = 'Alice'(name 有索引) (1) ______ 覆盖索引,无需回表
SELECT * FROM orders GROUP BY status (2) ______ 使用了临时表解决分组
SELECT * FROM orders ORDER BY created_at(无对应索引) (3) ______ 无法利用索引顺序,需要额外排序

提示: 回忆 Extra 常见值表格中的映射关系。


三、简答题(1道)

S1

以下是 JOIN 查询的 EXPLAIN 输出片段:

| id | select_type | table | type  | key         | rows | filtered | Extra                           |
|----|-------------|-------|-------|-------------|------|----------|---------------------------------|
| 1  | PRIMARY     | u     | ref   | idx_status  | 500  | 100      | Using index condition           |
| 1  | PRIMARY     | o     | eq_ref| PRIMARY     | 1    | 100      | Using where                     |

JOIN 语句原型:

SELECT u.name, o.amount
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.status = 1
ORDER BY o.created_at DESC
LIMIT 10;

请综合分析:

  1. 解释第一行(u 表)和第二行(o 表)中各字段的含义
  2. 指出潜在的优化点
  3. 说明为什么这里看不到 Using filesort 但却暗示了排序问题

答题框架提示: 逐行分析 type/key/rows/filterd/Extra → 结合原 SQL 的 WHERE 和 ORDER BY → 推断 EXPLAIN 输出了什么没输出的信息。


参考答案与解析

选择题答案

题号 正确答案 解析
Q1 B EXPLAIN 在执行前先模拟优化器的决策过程,生成 SQL 的执行计划,揭示索引使用、表连接顺序、临时表和文件排序等关键信息。A 错在 EXPLAIN 不真正执行 SQL;C 错在语法检查是 PREPARE 阶段的事;D 错在 EXPLAIN 不涉及锁操作。
Q2 C const 表示最多匹配一行,通过主键/唯一索引一次性定位,性能最优。range 是范围扫描,性能中等;ALL 是最差的全表扫描;ref 是非唯一索引的多行匹配,介于 range 和 eq_ref 之间。
Q3 A index 类型虽然是全表级别的扫描,但它只扫描索引树(通常有序排列,可利用顺序 IO),而 ALL 需要逐行扫描数据行(随机 IO),随机 IO 比顺序 IO 慢数十倍。B 错在 index 不是新增特性;C 错在两引擎都有全表扫描;D 错在 ICP 与 type 无关。
Q4 C possible_keys 表示优化器在评估阶段认为可能用到的候选索引列表,只是候选;key 才是最终实际选择的索引。如果 key 为 NULL 但 possible_keys 不为 NULL,说明优化器认为虽然有可选索引但当前情况下全表扫描更划算(例如表太小或条件选择性差),不一定是选错了。A 说反了;B 错在两者功能不同。
Q5 C YEAR(created_at) 函数作用于字段本身会导致索引失效(函数表达式不能被索引直接使用),因此仍然需要对结果做 WHERE 过滤;同时由于 created_at 上的值经过函数处理后无法利用索引排序,可能需要 filesort。正确的优化方式是改用范围查询:WHERE created_at >= '2025-01-01' AND created_at < '2026-01-01'。A 是 ICP 的标识不符合此场景;B 的 Using temporary 通常出现在 GROUP BY/DISTINCT 场景;D 的 Impossible where 发生在条件永远不成立时。
Q6 B rows 是优化器基于统计采样估算的需要扫描的行数(越小越好),filtered 是按表条件筛选后留存行的百分比(0~100),两者相乘 ≈ 实际需要处理的行数。这两个值是估算值,并非精确计数。A 错在 rows 不是总行数;C 错在 rows 是估算值而非实际值;D 错在完全理解偏差。

填空题答案

题号 答案 解析
F1 eq_ref;index 完整排序:system > const > eq_ref > ref > range > index > ALL。eq_ref 是每个前一表行通过唯一索引精确匹配一行,index 是全索引扫描(只扫索引树)。
F2 1;0;4 name 是 VARCHAR(64) NOT NULL:变长字段需要 1 字节标记实际长度,NOT NULL 故无 NULL 标记 = 256 + 1 + 0 = 257... 等等——实际上 utf8mb4 的 VARCHAR(64) 最大 256 字节,加上长度标记 1 字节,加上 NOT NULL 无 NULL 标记 0 字节 = 257 字节。但原文给的是 258,这意味着原文将 NULL 标记也算上了(即 name 为 nullable)。按照原文的数字:258 = 256 + 1 + 1(含 NULL 标记),那么题目应调整为 name 允许 NULL。按照原文给出的公式 258 = 64*4 + 1 + 1 来计算。age 是 INT NOT NULL = 4 字节(固定长度,无 NULL 标记)。
F3 (1) Using index;(2) Using temporary;(3) Using filesort Using index 表示覆盖索引无需回表;Using temporary 表示 GROUP BY/DISTINCT 使用了临时表;Using filesort 表示无法利用索引排序,需要在内存或磁盘中做额外排序操作。

简答题参考答案

S1 参考答案要点:

  1. 第一行(users u 表):

    • type=ref:通过非唯一索引 idx_status 进行等值查找(status = 1),匹配多行
    • key=idx_status:实际使用了 status 索引
    • rows=500:优化器估算扫描约 500 行
    • filtered=100%:所有扫描的行都满足条件
    • Extra: Using index condition:ICP 生效,在存储引擎层提前过滤
  2. 第二行(orders o 表):

    • type=eq_ref:对 u 表的每一行,通过 o.user_id = u.id(外键/主键)精确匹配一条订单记录
    • key=PRIMARY:通过主键精确查找
    • rows=1:每行精确匹配 1 条记录
    • Extra: Using where:存储引擎返回数据后,Server 层再做 WHERE 过滤
  3. 潜在优化点:

    • ORDER BY o.created_at DESC 没有出现在 EXPLAIN 的 Extra 中是因为 EXPLAIN 只显示了 JOIN 的部分,ORDER BY 导致的 filesort 可能在 LIMIT 之前执行
    • 应在 orders(created_at) 或 (user_id, created_at) 上建立索引来消除排序开销
    • 小表驱动大表:users 表 500 行驱动 orders 表的 eq_ref 是合理策略
  4. filesort 的隐含:

    • LIMIT 10 配合 ORDER BY 如果没有合适索引就需要 filesort
    • EXPLAIN 的输出截断了 JOIN 之后的步骤,完整的执行流程还会检查是否需要额外排序
    • 检查时应该看 EXPLAIN ANALYZE(MySQL 8.0.18+)或者观察执行时的 actual rows 来判断 filesort 是否发生

评分标准:逐行字段解释(×2 行各 2 分)+ 优化建议(2 分)+ filesort 分析(2 分)= 满分 8 分。答出关键点即可。

关联笔记