跳到主要内容
约 5 分钟4 张卡

执行计划

执行计划描述查询的访问路径和操作顺序。本文负责 MySQL 8.0 的 EXPLAIN 字段与实测对照。

1. 面试核心​

先记住这几条,再读正文
  • 候选与实际索引。 possible_keys 是候选索引,key 是实际选中的索引。卡 · 面试核心
  • 行数估计。 rows × filtered / 100 估计本次访问传给后续连接的行数。卡 · 实际统计与估算偏差
  • 实际执行统计。 EXPLAIN ANALYZE 真正执行语句,多次循环的时间按次平均。卡 · 实际统计与估算偏差
  • 额外排序。 Using filesort 表示需要额外排序,无法直接利用当前访问路径的顺序。排序数据可以留在内存,名称中的 file 不代表必然写磁盘。
  • 访问类型。 eq_ref 对每个外层行组合至多匹配一行,ref 可匹配多行。卡 · 访问类型

2. 查询条件与访问路径​

下面的查询按用户过滤,再取最近的记录。

CREATE TABLE orders_plan (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL,
created_at TIMESTAMP NOT NULL,
amount DECIMAL(10,2) NOT NULL,
KEY idx_user_created (user_id, created_at)
);
EXPLAIN SELECT id, created_at FROM orders_plan
WHERE user_id = 7 ORDER BY created_at LIMIT 20;

user_id 等值限定索引前缀。该前缀内的记录按 created_at 排列。二级索引还包含主键 id,因此满足取列需求。实际计划由数据分布、统计信息和成本估算决定。

3. 执行计划字段​

阅读传统 EXPLAIN 时,先看实际索引和访问类型。再用行数估计判断扫描范围,最后看附加操作。

字段含义
possible_keys能参与查找的候选索引。
key优化器实际选择的索引。
key_len所用索引键的最大长度,单位为字节。
rows单次访问预计检查的行数。
filtered表条件过滤后预计保留的百分比。
Extra覆盖、下推、排序和临时表等信息。

key_len 需结合列类型、字符集和可空性解释。可空列还包含 NULL 标记开销。它只能辅助判断所用键部分,实际扫描量需另看行数。

Using index 表示查询所需信息可由索引提供。Using index condition 表示在索引层先过滤,再按需取行。两者的执行区别见联合索引与覆盖索引。

Using filesort 表示额外排序。内存充足时,排序可在内存中完成。Using temporary 表示需要内部临时表。它同样需要结合实际内存和落盘指标判断。

4. 访问类型​

访问类型描述如何取得行,仍需结合扫描量判断成本。

类型访问方式
const通过主键或非空唯一键与常量比较,至多取得一行。
eq_ref每个外层行组合通过完整唯一键,至多匹配一行。
ref按非唯一键或索引前缀等值查找,可匹配多行。
range扫描索引中的一个或多个范围。
index扫描索引树,仍需检查大量条目。
ALL扫描整张表。

覆盖的 index 扫描能少读一些列。它仍需遍历索引,不能仅凭索引名认定查询高效。连接顺序还会放大内层调用次数。

5. 实际统计与估算偏差​

MySQL 8.0.18 起支持 EXPLAIN ANALYZE。它展示实际执行树,8.0.21 起可显式指定 FORMAT=TREE。actual time=a..b 的单位是毫秒。a 是取得首行的时间,b 是完成迭代器的时间。存在多次循环时,时间和输出行数按次平均。

父节点时间包含子节点。整棵树的节点时间相加会重复计算。内层每次只需 0.1ms,循环 10000 次也会累积成本。rows × loops 可辅助估算总输出量。

EXPLAIN ANALYZE 会运行语句。MySQL 8.0 还支持它分析多表 UPDATE 和 DELETE。这些语句会修改数据,实验应使用独立练习库。

估算偏差大时,检查实际参数和数据倾斜。再检查采样统计是否过时、过滤列是否相关。ANALYZE TABLE 更新统计信息。它与执行查询计时的 EXPLAIN ANALYZE 分属不同操作。

直方图可补充单列值分布。多列相关性仍需用实际查询验证。

6. 使用边界​

小表全表扫描省去了额外索引定位成本。返回大部分行时,顺序扫描也有成本优势。判断优化收益应使用相同参数、数据规模和缓存条件。

执行计划主要解释数据库内部访问。锁等待、客户端取数和网络传输还需运行指标。索引变更的写入代价由索引设计评估。

7 · 面试题卡片​

本页题目与参考回答

7.1 查询使用了索引为什么仍然慢,如何用执行计划继续定位?​

考察重点:访问范围、估算偏差和运行等待。

相关内容:面试核心、查询条件与访问路径、使用边界。

参考回答

索引扫描范围过大时,查询仍会慢。

大量回表、排序和连接循环也会增加成本。先确认实际 key、访问类型和估计行数。再在受控环境用 EXPLAIN ANALYZE 对照实际统计。锁等待和网络耗时需结合运行指标检查。

7.2 EXPLAIN ANALYZE 中单次耗时很低,为什么整个连接仍慢?​

考察重点

  • 迭代器循环次数
  • 包含关系与平均时间

相关内容:实际统计与估算偏差。

参考回答

内层迭代器被反复调用,会累积单次耗时。

loops 表示调用次数,多次调用的时间按次平均。父节点已包含子节点,时间应按包含关系阅读。减少外层输入行数,可以直接减少内层调用。

7.3 估计行数与实际行数相差很大时,应该先检查什么?​

考察重点

  • 统计信息和数据倾斜
  • 可比条件下的验证

相关内容:实际统计与估算偏差、使用边界。

参考回答

先核对绑定参数、数据分布和统计更新时间。

热点值和相关列都会造成估算偏差。按需用 ANALYZE TABLE 更新统计,再比较同一查询。直方图补充单列分布,多列相关性还需实测。

7.4 EXPLAIN 的 eq_ref、ref、range、index 和 ALL 有何区别?​

考察重点:访问方式、唯一键与扫描范围。

相关内容:访问类型。

参考回答

这些访问类型描述定位和扫描记录的方式,成本还要结合扫描量、覆盖情况与循环次数判断。

  • eq_ref 是唯一键等值查找。 每个外层行组合至多匹配一行。
  • ref 是非唯一的等值查找。 每个外层行组合可匹配多行。
  • range 扫描指定索引范围。 它只访问选定区间。
  • index 和 ALL 分别扫描索引树与整表。 前者是否更省成本,还取决于索引宽度和覆盖情况。

8. 参考资料​