D
RuntimeScope/MySQL 面试专题/explain/SQL 优化与 EXPLAIN

参考:高性能 MySQL(第 4 版) · MySQL 技术内幕:InnoDB 存储引擎(第 2 版) · MySQL 8.4 Reference Manual。每章含可暂停 Lab 动画 + 面试问答 + 书籍章节对照。

MySQL 面试 · Lab D · SQL 优化与 EXPLAIN

SQL 优化与 EXPLAIN

主问题

EXPLAIN SELECT * FROM orders WHERE user_id = 1001 AND status = 'PAID' ORDER BY created_at DESC LIMIT 20;

Extra 出现 Using filesort,你怎么读、怎么优化?

这题真正考什么

  • · 不是考你背 type / key / rows / Extra 的字段定义。
  • · 而是考你能不能把 EXPLAIN 翻译成访问路径,并闭环到索引设计。

学完你要能做到

  • · 把 EXPLAIN 字段翻译成访问路径
  • · 算出 rows × filtered 的估算行数
  • · 知道 Using filesort 为什么出现、怎么消掉
  • · 用联合索引同时服务过滤和排序
30 秒答案先记结论,再看推导

Using filesort 说明 ORDER BY created_at 没走索引顺序、需要额外排序。

建一个 (user_id, status, created_at) 联合索引, 等值过滤后正好按 created_at 有序读取。

排序由索引顺序提供,filesort 就消失了。

学习路线

点节点跳转 · 滚动时自动高亮当前位置

01

这道题到底在问什么?

表面问「Using filesort 怎么优化」,实际是在问:你能不能把 EXPLAIN翻译成访问路径,再闭环到索引设计。

EXPLAIN SELECT * FROM orders
WHERE user_id = 1001 AND status = 'PAID'
ORDER BY created_at DESC LIMIT 20;

两个等值过滤 + 一个排序。痛点在 Extra 的 Using filesort——排序没能靠索引顺序完成。

02

怎么读一份 EXPLAIN?

别逐字段背,抓住这三组,就能把计划翻译成访问路径。

type & key

访问方式 + 实际索引

type 反映访问方式好坏(const/ref/range/ALL);key 是优化器最终选的索引,不等于 possible_keys。

rows & filtered

估算扫描 + 过滤比例

都是优化器估算。rows×filtered/100 才是估算过滤后行数,实际返回还要看 LIMIT。

Extra

执行细节信号

Using index = 覆盖;Using filesort = 排序没走索引;Using temporary = 用了临时表。

03

rows 和 filtered 怎么看?

这两个都是优化器的估算,不是真实返回行数。

rows10000×filtered10%=估算过滤后≈ 1000 行
结论rows × filtered / 100 才是估算过滤后的行数;实际返回还要看 LIMIT 和真实数据。
04

优化前后对比(Lab)

切换「优化前 / 优化后」,逐步对照同一条 SQL 的两份执行计划。

慢 SQL · orders 表

按 user_id + status 过滤,按 created_at 倒序取前 20 条

BadImproved
SELECT * FROM orders
WHERE user_id = 1001 AND status = 'PAID'
ORDER BY created_at DESC
LIMIT 20;

Bad plan · 仅有 user_id 单列索引

当前展示
key
idx_user_id
possible_keys
idx_user_id
rows
≈ 8000
filtered
10%
Extra
Using where; Using filesort
访问路径
idx_user_id 命中
Server 过滤 status
filesort 排序
LIMIT 20 截断

先扫描该用户全部订单 (~8000 行),Server 层按 status 过滤,最后在内存里按 created_at 排序。

Improved plan · 新增联合索引

对比参考
key
idx_orders_user_status_created
possible_keys
idx_user_id, idx_orders_user_status_created
rows
≈ 40
filtered
100%
Extra
Using where
访问路径
联合索引精确命中
索引顺序 = 排序顺序
filesort 已消除
LIMIT 20 顺序读完即停

联合索引按 (user_id, status) 定位到子树,叶子已按 created_at DESC 排好,顺序取前 20 条。

rows(估算扫描)
≈ 8000≈ 40
Extra
Using filesorteliminated
key(实际索引)
idx_user_ididx_orders_user_status_created
rows ≠ LIMIT 返回行数

EXPLAIN 的 rows 是优化器估算扫描行数,不是查询最终返回的 20 行。LIMIT 命中的实际返回行数靠 LIMIT 子句和真实数据决定。

SELECT * 不等于覆盖索引

只要 SELECT 的列没有全部出现在二级索引里,就会回表到聚簇索引取整行。Extra 里出现Using index才是真正的覆盖索引。

当前发生了什么

慢查询定位到这条 SQL,先用 EXPLAIN 看执行计划,而不是凭感觉加索引。

为什么会这样

优化的第一步是读懂优化器打算怎么访问数据。

面试怎么回答

我会先从慢日志 / APM 定位具体 SQL 和时间窗口,再 EXPLAIN。

常见误区

误区:一看慢就加索引,不先看执行计划。

结论读 EXPLAIN 的顺序:key 命中没 → rows×filtered 多少 → Extra 有没有 filesort/临时表。
05

联合索引怎么消掉 filesort?

把等值列放前、排序列放最后,让一个索引同时管过滤和排序。

优化前Using filesort
  1. 1WHERE 只命中部分索引
  2. 2扫描较多行 / 回表
  3. 3ORDER BY created_at 现场排序
优化后索引提供顺序
  1. 1建 (user_id, status, created_at)
  2. 2等值定位 + 有序读取
  3. 3排序由索引顺序提供,filesort 消失
结论联合索引列序口诀:等值列在前精确定位,排序列在后提供顺序。
06

Extra 信号对比

四个最常见的 Extra 信号,记住「好 / 中性 / 想消掉」。

Extra 信号含义好还是坏
Using index覆盖索引,免回表
Using whereServer 层还要再过滤一遍中性
Using filesort排序没法靠索引顺序完成想办法消掉
Using temporary用了临时表(GROUP BY / DISTINCT / 排序)较重,尽量避免
07

面试回答生成器

按时间预算选一档念出来。

Using filesort 说明 ORDER BY created_at 没走索引顺序、需要额外排序。

建一个 (user_id, status, created_at) 联合索引,让等值过滤后正好按 created_at 有序读取,filesort 就消失了。

08

追问链 + 复习清单

勾掉每一条,这道题就算通关。

通关检查

0/5 已掌握

追问链

  • QUsing index、Using where、Using temporary 分别意味着什么?
  • Q(user_id,status,created_at) 和 (status,user_id,created_at) 怎么选?
  • QEXPLAIN ANALYZE 和普通 EXPLAIN 的差异是什么?