EXPLAIN 执行计划分析:type / key / rows / Extra 解读 + 慢查询定位

一句话:EXPLAIN 是 MySQL 中用于分析查询执行计划的命令,可以帮助我们理解查询是如何执行的,识别性能瓶颈,优化慢查询。

1. EXPLAIN 基础

1.1 使用方法

 
-- 基本语法
 
EXPLAIN SELECT * FROM users WHERE id = 1;
 
-- 查看JSON格式(更详细)
 
EXPLAIN FORMAT=JSON SELECT * FROM users WHERE id = 1;
 
-- 查看树形格式(MySQL 8.0+)
 
EXPLAIN FORMAT=TREE SELECT * FROM users WHERE id = 1;
 

1.2 执行计划解读顺序


graph TD

    A[EXPLAIN结果] --> B[id: 查询序号]

    A --> C[select_type: 查询类型]

    A --> D[table: 表名]

    A --> E[type: 访问类型]

    A --> F[key: 使用的索引]

    A --> G[rows: 扫描行数]

    A --> H[Extra: 额外信息]

    style E fill:#e1f5fe

    style F fill:#e8f5e8

2. 关键字段解读

2.1 type(访问类型)

重要性:⭐⭐⭐⭐⭐(最重要的字段之一)

type值说明性能示例
system表只有一行记录⭐⭐⭐⭐⭐系统表
const通过主键或唯一索引查找⭐⭐⭐⭐⭐WHERE id = 1
eq_ref关联查询中使用主键或唯一索引⭐⭐⭐⭐JOIN ON a.id = b.id
ref使用非唯一索引查找⭐⭐⭐WHERE name = 'Alice'
range索引范围扫描⭐⭐⭐WHERE id > 100
index全索引扫描⭐⭐SELECT id FROM users
ALL全表扫描SELECT * FROM users

优化目标:至少达到 ref 级别,避免 ALL

2.2 key(使用的索引)

 
-- 显示查询实际使用的索引
 
key: PRIMARY          -- 使用主键索引
 
key: idx_name         -- 使用普通索引
 
key: NULL             -- 未使用索引
 

重要性:⭐⭐⭐⭐

2.3 rows(扫描行数)

 
-- 预估需要扫描的行数
 
rows: 1000            -- 需要扫描1000行
 
rows: 1               -- 只需要扫描1行
 

重要性:⭐⭐⭐(越小越好)

2.4 Extra(额外信息)

重要性:⭐⭐⭐⭐

Extra值说明性能
Using index覆盖索引,无需回表⭐⭐⭐⭐⭐
Using where在存储引擎层过滤后,还需在Server层过滤⭐⭐⭐
Using temporary使用临时表⭐⭐
Using filesort使用文件排序⭐⭐
Select tables optimized away优化器已优化⭐⭐⭐⭐⭐

3. 常见执行计划分析

3.1 简单查询

 
EXPLAIN SELECT * FROM users WHERE id = 1;
 

结果解读

  • type: const(主键查找)

  • key: PRIMARY(使用主键)

  • rows: 1(只扫描1行)

  • Extra: NULL(无额外信息)

3.2 范围查询

 
EXPLAIN SELECT * FROM users WHERE age > 25 AND age < 30;
 

结果解读

  • type: range(范围扫描)

  • key: idx_age(使用age索引)

  • rows: 100(预估扫描100行)

  • Extra: Using where(需要额外过滤)

3.3 关联查询

 
EXPLAIN
 
SELECT u.name, o.order_date
 
FROM users u
 
INNER JOIN orders o ON u.id = o.user_id
 
WHERE u.age > 25;
 

结果解读

  • 两个表都有记录

  • type: ref(使用索引关联)

  • key: PRIMARY, idx_user_id(两个索引)

  • rows: 100, 500(扫描行数)

  • Extra: Using join buffer(使用连接缓冲)

4. 慢查询定位

4.1 开启慢查询日志

 
-- 查看慢查询配置
 
SHOW VARIABLES LIKE 'slow_query%';
 
SHOW VARIABLES LIKE 'long_query_time';
 
-- 开启慢查询日志
 
SET GLOBAL slow_query_log = 'ON';
 
SET GLOBAL long_query_time = 2;  -- 超过2秒记录
 

4.2 分析慢查询

 
-- 使用mysqldumpslow工具
 
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
 
-- 使用pt-query-digest工具
 
pt-query-digest /var/log/mysql/slow.log
 

4.3 优化慢查询

 
-- 1. 添加索引
 
ALTER TABLE users ADD INDEX idx_age (age);
 
-- 2. 重写查询
 
-- 原查询
 
SELECT * FROM users WHERE YEAR(create_time) = 2026;
 
-- 优化后
 
SELECT * FROM users WHERE create_time >= '2026-01-01' AND create_time < '2027-01-01';
 
-- 3. 分页优化
 
-- 原查询(慢)
 
SELECT * FROM users ORDER BY id LIMIT 100000, 10;
 
-- 优化后(快)
 
SELECT * FROM users WHERE id > 100000 ORDER BY id LIMIT 10;
 

5. 高级分析

5.1 JSON格式分析

 
EXPLAIN FORMAT=JSON SELECT * FROM users WHERE id = 1\G
 

关键字段

  • query_block:查询块

  • table:表信息

  • access_type:访问类型

  • possible_keys:可能使用的索引

  • key:实际使用的索引

  • rows:扫描行数

  • filtered:过滤百分比

5.2 树形格式分析(MySQL 8.0+)

 
EXPLAIN FORMAT=TREE SELECT * FROM users WHERE id = 1\G
 

输出示例

 
-> Index lookup on users using PRIMARY  (cost=0.35 rows=1)
 

7. 优化案例

案例1:全表扫描优化

 
-- 原查询(慢)
 
EXPLAIN SELECT * FROM users WHERE name = 'Alice';
 
-- type: ALL, rows: 100000
 
-- 优化后
 
ALTER TABLE users ADD INDEX idx_name (name);
 
EXPLAIN SELECT * FROM users WHERE name = 'Alice';
 
-- type: ref, rows: 1
 

案例2:文件排序优化

 
-- 原查询(慢)
 
EXPLAIN SELECT * FROM users ORDER BY name;
 
-- Extra: Using filesort
 
-- 优化后
 
ALTER TABLE users ADD INDEX idx_name (name);
 
EXPLAIN SELECT * FROM users ORDER BY name;
 
-- Extra: Using index
 

案例3:分页查询优化

 
-- 原查询(慢)
 
EXPLAIN SELECT * FROM users ORDER BY id LIMIT 100000, 10;
 
-- rows: 100010
 
-- 优化后
 
EXPLAIN SELECT * FROM users WHERE id > 100000 ORDER BY id LIMIT 10;
 
-- rows: 10
 

核心要点

 
-- 执行EXPLAIN
 
EXPLAIN SELECT * FROM users WHERE id = 1;
 
-- 关键字段
 
type: 访问类型(至少ref级别)
 
key: 使用的索引
 
rows: 扫描行数(越小越好)
 
Extra: 额外信息(避免Using filesort/temporary)
 
-- 慢查询优化
 
1. 添加索引
 
2. 重写查询
 
3. 分页优化
 
4. 避免SELECT *
 

▶ 对应原理:26-EXPLAIN解读

速记卡(面试闪卡)

Q1:一句话讲清「EXPLAIN 执行计划分析:type / key / rows / Extra 解读 + 慢查询定位」到底是什么?

A:EXPLAIN 是 MySQL 给你的查询”体检报告”:不真跑 SQL,只说优化器打算怎么执行、哪里会慢。

Q2:四张成绩单怎么读 —— 怎么理解?

A:像看体检单:type 是”运动能力等级”(const 满分级、ALL 全表扫最差),key 是”用了哪台仪器”(NULL 就是没用上索引),rows 是”估算要查多少人”(越小越好),Extra 是”医嘱备注”——Using index 是免回表的福报,Using filesort 是排序在拖后腿。

Q3:怎么用它抓慢查询 —— 怎么理解?

A:像查水管漏点:先 EXPLAIN 看 type 是不是 ALL(全表扫=漏水主因)、key 是不是 NULL(索引没生效)、rows 是不是大得离谱。再配合 slow query log(慢查询日志,记录超过阈值的 SQL)和 FORMAT=JSON 看执行细节,定位后要么加索引、要么改 SQL。

Q4:覆盖索引与回表 —— 怎么理解?

A:覆盖索引(Covering Index)= 你要的列全在索引里,护士直接把药递给你,不用再去药房跑一趟(回表)。回表 = 二级索引只记了主键,要拿别的列得拿主键去聚簇索引再查一次,多一次 IO。所以建联合索引尽量把要查的列包进去,Extra 里看到 Using index 就是建对了。

Q5:type 梯度怎么记 —— 怎么理解?

A:type 像电梯楼层:system/const(直达顶层,主键精确命中)> eq_ref(JOIN 用主键)> ref(普通索引)> range(范围扫)> index(扫全索引)> ALL(爬楼梯全表扫)。记住”能 const 不 ref,能 ref 不 ALL”,面试常问 const 和 ref 差在哪——前者唯一索引命中一行,后者普通索引可能多行。

Q6:核心速记主线有哪些?

  • EXPLAIN 只分析计划、不真跑 SQL

  • 四字段:type / key / rows / Extra

  • type 梯度 const>ref>range>index>ALL

  • 覆盖索引免回表(Using index),配慢查询日志定位

口诀

A:EXPLAIN 体检不真跑,四字段记牢 type key rows Extra

type 梯度 const 最美,ALL 全表最该逃

key 为 NULL 索引没用上,filesort 见 Extra 要优化

覆盖索引 Using index,免回表来速度快

相关链接

相关链接