执行计划
执行计划描述查询的访问路径和操作顺序。本文负责 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分别扫描索引树与整表。 前者是否更省成本,还取决于索引宽度和覆盖情况。