Skip to main content
Documents
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

MySQL EXPLAIN

官方文档:

其它文档:

MySQL EXPLAIN 是一个用于分析 SQL 查询执行计划的工具,它模拟优化器执行查询,不会实际运行,而是返回关于表访问顺序、索引使用、扫描行数等性能相关的信息,帮助识别和优化性能瓶颈。

EXPLAIN 的作用

  • 显示 MySQL 如何处理 SQL 语句。
  • 分析查询语句或表结构的性能瓶颈。
  • 查看优化器选择的执行路径是否符合预期。

EXPLAIN 输出的主要字段 (示例)

  • id: 查询的序列号。
  • select_type: 查询类型(SIMPLE, PRIMARY, SUBQUERY, UNION 等)。
  • table: 表名或别名。
  • type: 访问类型(从好到坏:system, const, eq_ref, ref, range, index, ALL)。
  • possible_keys: 可能用到的索引。
  • key: 实际使用的索引。
  • key_len: 使用的索引长度。
  • ref: 与索引比较的列或常量。
  • rows: 估算的扫描行数。
  • Extra: 额外信息(如 Using index, Using filesort, Using temporary 等)。

使用示例

EXPLAIN SELECT * FROM users WHERE status = 'active' AND created_at > '2025-01-01';

一句话定位

EXPLAIN 是 MySQL 执行计划分析工具,用来判断 SQL 有没有走索引、怎么走、代价多大、慢在哪里。

一、EXPLAIN 是什么?解决什么问题?

EXPLAIN 用于回答 6 个核心问题:

  1. 用了哪些表?
  2. 表的访问顺序?
  3. 用没用索引?用的是哪个?
  4. 是全表扫描还是索引扫描?
  5. 预计扫描多少行?
  6. 有没有额外开销(排序、临时表、回表等)?

二、EXPLAIN 基本用法

EXPLAIN SELECT * FROM user WHERE id = 1;

MySQL 8 推荐:

EXPLAIN ANALYZE SELECT * FROM user WHERE id = 1;

EXPLAIN ANALYZE 会 真正执行 SQL,并给出真实耗时(非常重要)

三、EXPLAIN 输出字段总览(重点)

常见字段(按重要程度):

字段 重要性 含义
id ⭐⭐⭐ 查询层级 / 执行顺序
select_type ⭐⭐⭐ 查询类型
table ⭐⭐ 访问的表
type ⭐⭐⭐⭐ 访问方式(最重要)
possible_keys ⭐⭐ 可能使用的索引
key ⭐⭐⭐⭐ 实际使用的索引
key_len ⭐⭐ 索引使用长度
ref ⭐⭐ 索引比较方式
rows ⭐⭐⭐ 预计扫描行数
filtered ⭐⭐ 条件过滤比例
Extra ⭐⭐⭐⭐ 额外信息(非常关键)

四、id:执行顺序(非常容易考)

规则总结

  • id 越大,越先执行
  • id 相同 -> 从上往下执行
  • 子查询 id > 外层查询

示例:

SELECT * FROM A 
WHERE id IN (
  SELECT id FROM B
);

执行顺序:

  1. 子查询 B(id=2)
  2. 外层查询 A(id=1)

五、select_type:查询类型

类型 说明
SIMPLE 简单查询(无子查询)
PRIMARY 主查询
SUBQUERY 子查询
DERIVED 派生表(FROM 子查询)
UNION UNION 中的第二个查询
UNION RESULT UNION 合并结果

⚠️ DERIVED 常是性能风险点(会生成临时表)

六、type:访问方式(最重要字段)

从好到坏排序:

type 说明
system 系统表
const 主键 / 唯一索引等值
eq_ref 唯一索引 JOIN
ref 非唯一索引
range 范围扫描
index 全索引扫描
ALL 全表扫描 ❌

记忆口诀:

const > eq_ref > ref > range > index > ALL

示例对比

✅ 好 SQL

SELECT * FROM user WHERE id = 1;
type = const

⚠️ 可接受

SELECT * FROM user WHERE age BETWEEN 20 AND 30;
type = range

❌ 危险

SELECT * FROM user WHERE name LIKE '%abc';
type = ALL

七、possible_keys & key:有没有“选错索引”

  • possible_keys:理论上能用的索引
  • key:实际用到的索引

⚠️ 如果:

possible_keys = idx_a, idx_b
key = NULL

说明:优化器判断全表扫描更快(很危险)

八、key_len:索引用了多少?

  • 反映 联合索引命中到哪一列
  • 单位是 字节

例子:

INDEX idx(a, b, c)
WHERE a=1 AND b=2

-> key_len = a + b

⚠️ 用于判断 最左前缀是否完整使用

九、ref:索引匹配方式

常见值:

ref 值 含义
const 常量匹配
table.col 表字段比较
func 函数计算(通常不好)

十、rows:扫描行数(越小越好)

  • 这是优化最直观指标
  • SQL 优化的核心目标:减少 rows

⚠️ rows 很小但 SQL 慢:

  • 回表多
  • filesort
  • 临时表
  • 网络 IO

十一、filtered:过滤比例(MySQL 8 常见)

filtered = 10

表示:

扫描的 rows 中,只有 10% 能命中 WHERE 条件

rows × filtered ≈ 最终结果行数

十二、Extra:最重要的“危险信号”

✅ 好的 Extra

Extra 含义
Using index 覆盖索引(非常好)
Using where 使用了条件过滤
Using index condition ICP 下推

❌ 危险的 Extra(必须优化)

Extra 含义
Using filesort 额外排序(慢)
Using temporary 使用临时表
Range checked for each record 每行重新计算索引
Using join buffer Join 未走索引

十三、EXPLAIN ANALYZE(MySQL 8 强烈推荐)

EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 100;

输出:

  • 实际执行时间
  • 实际扫描行数
  • 真实成本

📌 对比 EXPLAIN 和 ANALYZE:

  • EXPLAIN -> 预估
  • ANALYZE -> 实际

十四、JOIN 的 EXPLAIN 读法

原则:

  • 小表驱动大表
  • JOIN 字段必须有索引
  • 被驱动表 type 至少 ref
SELECT *
FROM order o
JOIN user u ON o.user_id = u.id;

EXPLAIN 应该看到:

  • u.id 使用 PRIMARY
  • o.user_id 有索引

十五、典型慢 SQL 场景与 EXPLAIN 定位

1️⃣ LIKE ‘%xxx%’

type=ALL

解决:

  • 改成 xxx%
  • 使用全文索引
  • ES / 搜索引擎

2️⃣ WHERE 使用函数

WHERE DATE(create_time)='2024-01-01'

解决:

WHERE create_time >= '2024-01-01'
AND create_time < '2024-01-02'

3️⃣ ORDER BY filesort

解决:

  • where + order by 用同一个索引
  • 避免跨索引排序

4️⃣ OR 导致索引失效

解决:

  • 拆成 UNION
  • 使用同一索引字段

十六、EXPLAIN 优化口诀

一看 type,二看 key,三看 rows,四看 Extra

目标:

  • type ≥ range
  • rows 越小越好
  • 没有 Using filesort / temporary
  • 能 Using index 就最好

十七、其它

Q:EXPLAIN 看哪些字段最重要?

A:type、key、rows、Extra

Q:type=ALL 一定慢吗?

A:大表一定慢,小表可接受

Q:Using index 一定最好吗?

A:是(覆盖索引,避免回表)

Q:EXPLAIN 能看到锁吗?

A:不能(需 performance_schema)

🔚 总结一句话

EXPLAIN 是 SQL 性能的“X 光片”,真正的高手不是会写 SQL,而是会读 EXPLAIN。