Explain关键字?

EXPLAIN 关键字:MySQL 查询执行计划的 "透视镜"

你想深入了解EXPLAIN关键字,它是 MySQL 中分析查询性能、排查索引失效、优化 SQL 的核心工具 ------ 通过EXPLAIN可以 "看透" MySQL 优化器如何执行你的 SQL 语句,包括是否使用索引、扫描行数、连接方式等关键信息,无需实际执行查询就能定位性能问题。

下面我会从基本用法输出字段详解实战案例进阶用法 四个维度,把EXPLAIN讲透,让你能独立分析任意 SQL 的执行计划。

一、EXPLAIN 基本用法

1. 核心作用

  • 显示 MySQL 执行 SQL 时的执行计划(而非执行结果);
  • 告诉我们:SQL 将如何扫描表、是否使用索引、扫描多少行、表之间如何连接等;
  • 支持SELECTDELETEINSERTUPDATE(MySQL 8.0+),最常用的是SELECT

2. 基础语法

sql 复制代码
-- 最基础用法:分析SELECT语句
EXPLAIN SELECT * FROM user WHERE name = '张三';

-- 简写(效果相同)
DESC SELECT * FROM user WHERE name = '张三';

-- MySQL 8.0+支持分析UPDATE/DELETE
EXPLAIN UPDATE user SET age = 21 WHERE id = 1;

执行后会返回一个表格(通常 1 行或多行,对应多表连接),每行包含 12 个字段(MySQL 5.7+),每个字段都有明确的性能含义。

二、EXPLAIN 输出字段详解(按重要性排序)

为了方便理解,先给出一个典型的执行计划示例:

sql 复制代码
EXPLAIN SELECT id, name FROM user WHERE name = '张三';

输出结果(核心字段):

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE user ref idx_name idx_name 62 const 2 Using index

下面逐个解析每个字段的含义,重点标注核心关注项

1. id:查询的执行顺序标识

  • 含义:表示查询中执行子查询 / 表连接的顺序,数字越大越先执行;

  • 规则

    • 相同 id:按从上到下顺序执行(多表连接);
    • 不同 id:id 值越大,执行优先级越高;
    • NULL:表示这是一个临时表 / 汇总结果(如GROUP BY的临时表)。
  • 示例

    sql 复制代码
    -- 子查询,id=2先执行,再执行id=1
    EXPLAIN SELECT * FROM user WHERE id = (SELECT id FROM order WHERE order_no = 'O123');

2. select_type:查询类型(判断查询复杂度)

核心值说明(按常见程度排序):

含义
SIMPLE 简单查询(无子查询、无 UNION),最常见
PRIMARY 主查询(包含子查询 / UNION 时,外层查询的类型)
SUBQUERY 子查询(内层查询,不依赖外层)
DERIVED 派生表(FROM 中的子查询,MySQL 会先执行并生成临时表)
UNION UNION 中的第二个及以后的查询
UNION RESULT UNION 的最终结果集

3. table:当前行对应的表 / 临时表

  • 显示执行计划对应的表名(如userorder);
  • 若为派生表,显示derivedN(N 为 id 值);
  • 若为 UNION 结果,显示unionM,N(M、N 为 id 值)。

4. type:访问类型(核心!判断索引是否生效)

含义 :MySQL 扫描表中数据的方式,是判断查询性能的最核心指标,从优到劣排序如下:

类型 含义 性能等级 说明
system 表只有 1 行数据(系统表),极致优化(几乎遇不到) 最优 特殊情况,比 const 更好
const 通过主键 / 唯一索引查询,最多匹配 1 行 最优 WHERE id = 1
eq_ref 多表连接时,被驱动表通过主键 / 唯一索引匹配(如JOIN t2 ON t1.id=t2.id 优秀 关联查询的理想类型
ref 通过非唯一索引匹配,返回多行(如WHERE name = '张三' 良好 索引生效的常见类型
ref_or_null 类似 ref,但包含 NULL 值查询(如WHERE name = '张三' OR name IS NULL 良好 略逊于 ref
range 索引范围扫描(如WHERE id BETWEEN 1 AND 10IN (1,2,3) 一般 索引生效,但扫描范围较大
index 扫描整个索引树(未回表,如覆盖索引) 较差 比 ALL 好,但仍扫描全索引
ALL 全表扫描(Full Table Scan) 最差 索引失效,性能暴跌

核心判断

  • ✅ 理想情况:const/eq_ref/ref(索引高效生效);
  • ⚠️ 需要优化:range(尽量缩小范围);
  • ❌ 必须优化:index/ALL(索引失效,全表 / 全索引扫描)。

5. possible_keys:可能使用的索引

  • 含义 :MySQL 优化器认为该查询可能用到的索引列表(仅为候选,不一定实际使用);
  • 若为NULL:表示没有可用的索引(大概率会全表扫描)。

6. key:实际使用的索引(核心!)

  • 含义:MySQL 优化器最终选择的索引名称;
  • 若为NULL:表示未使用任何索引(索引失效);
  • 重点:possible_keys有值但keyNULL,说明优化器判断走索引成本更高,主动放弃(如匹配行数太多)。

7. key_len:使用的索引长度(字节)

  • 含义:表示 MySQL 使用的索引字段的总长度,可用于判断联合索引的生效范围;
  • 计算规则(示例):
    • varchar(20)(utf8):20×3 + 2(字符串长度标识)= 62 字节;
    • int:4 字节;
    • 联合索引idx_name_age(name, age):若key_len=66(62+4),说明两个字段都生效;若 = 62,说明仅 name 生效。

8. ref:与索引比较的列 / 常量

  • 含义 :表示与索引字段匹配的内容,分为两种:
    • const:匹配常量(如WHERE name = '张三',' 张三 ' 是常量);
    • 表名。字段名:多表连接时,匹配另一张表的字段(如t1.name = t2.name);
  • 若为NULL:表示没有使用等值匹配(如范围查询)。

9. rows:预估扫描行数(核心!)

  • 含义:MySQL 优化器预估的、执行查询需要扫描的行数(不是实际行数);
  • 数值越小越好:行数越多,说明需要扫描的数据越多,性能越差;
  • 注意:该值是基于表的统计信息估算的,可能与实际有偏差,可通过ANALYZE TABLE 表名更新统计信息。

10. Extra:额外信息(核心!)

含义:补充说明执行计划的关键细节,常见重要值如下:

含义 优化指导
Using index 使用覆盖索引(无需回表),性能极佳 理想状态,尽量保留
Using where 先扫描数据,再用 WHERE 过滤(可能索引失效) 需检查是否能用索引过滤
Using index condition 索引条件下推(ICP),先通过索引过滤部分数据,减少回表 优化器自动优化,正常
Using filesort 需额外排序(未使用索引排序),性能差 必须优化(加排序字段索引)
Using temporary 需创建临时表(如 GROUP BY/ORDER BY 字段无索引),性能极差 必须优化(加联合索引)
Using join buffer 多表连接时使用连接缓冲区(未用索引连接) 需优化连接字段的索引
Impossible WHERE WHERE 条件永远为假(如WHERE 1=0),无数据返回 检查 SQL 逻辑
Select tables optimized away 聚合函数(如 COUNT (*))直接通过索引统计获取,无需扫描数据 最优状态

三、EXPLAIN 实战案例(手把手分析)

案例 1:索引生效(理想情况)

sql 复制代码
-- 表结构:user(id PK, name 索引, age)
EXPLAIN SELECT id, name FROM user WHERE name = '张三';

输出核心字段分析:

type key rows Extra 结论
ref idx_name 2 Using index 索引生效,覆盖索引无回表

分析

  • type=ref:使用非唯一索引匹配,索引生效;
  • key=idx_name:实际使用 name 索引;
  • Extra=Using index:覆盖索引,无需回表;
  • rows=2:仅扫描 2 行,性能优秀。

案例 2:索引失效(全表扫描)

sql 复制代码
EXPLAIN SELECT * FROM user WHERE DATE(create_time) = '2025-12-27';

输出核心字段分析:

type key rows Extra 结论
ALL NULL 1000 Using where 索引失效,全表扫描

分析

  • type=ALL:全表扫描,索引失效;
  • key=NULL:未使用任何索引;
  • rows=1000:预估扫描 1000 行,性能差;
  • 原因:对索引字段create_time使用了DATE()函数,导致索引失效。

案例 3:联合索引部分生效

sql 复制代码
-- 联合索引:idx_a_b_c(a,b,c)
EXPLAIN SELECT * FROM user WHERE a = 1 AND c = 3;

输出核心字段分析:

key key_len Extra 结论
idx_a_b_c 4 Using where 仅 a 字段的索引生效,c 失效

分析

  • key=idx_a_b_c:使用了联合索引,但key_len=4(仅 a 字段的长度);
  • 原因:破坏了最左前缀原则(跳过 b 直接用 c),导致 c 的索引失效;
  • 优化:调整查询条件为a=1 AND b IS NOT NULL AND c=3,或修改联合索引为idx_a_c

四、EXPLAIN 进阶用法

1. EXPLAIN EXTENDED(MySQL 5.6+)

  • 额外输出filtered字段(过滤百分比),表示经过 WHERE 过滤后剩余的行数比例;

  • 可通过SHOW WARNINGS查看优化器改写后的 SQL(比如隐式转换、条件简化):

    sql 复制代码
    EXPLAIN EXTENDED SELECT * FROM user WHERE phone = 13800138000;
    SHOW WARNINGS; -- 会显示优化器将phone=13800138000改为phone=CAST(13800138000 AS CHAR)

2. EXPLAIN FORMAT=JSON(结构化输出)

  • 输出 JSON 格式的执行计划,包含更详细的成本计算(如cost_inforows_examined_per_scan);
  • 适合深度分析

3. 分析多表连接

多表连接时,EXPLAIN会返回多行(每张表一行),按id和执行顺序分析:

sql 复制代码
EXPLAIN SELECT * FROM user u JOIN `order` o ON u.id = o.user_id WHERE u.name = '张三';

重点关注:

  • 驱动表(先执行的表)的type是否为ref/const
  • 被驱动表的type是否为eq_ref(主键 / 唯一索引连接);
  • 避免type=ALL的表出现在连接的外层(会导致全表扫描后再连接)。

总结

  1. EXPLAIN 核心价值:无需执行 SQL,就能查看优化器的执行计划,定位索引失效、全表扫描、临时表 / 文件排序等性能问题;
  2. 核心关注字段type(访问类型,避免 ALL/index)、key(实际使用的索引,避免 NULL)、rows(预估扫描行数,越小越好)、Extra(是否有 Using filesort/Using temporary);
  3. 使用流程:写 SQL → EXPLAIN 分析 → 定位问题(如 type=ALL、key=NULL) → 优化(加索引 / 改 SQL) → 重新 EXPLAIN 验证。
相关推荐
海盗12342 小时前
MySQL主从复制的配置方式
android·mysql·adb
liang_jy5 小时前
Android 事件分发机制(二)—— 点击事件透传
android·面试·源码
圆号本昊7 小时前
Flutter Android Live2D 2026 实战:模型加载 + 集成渲染 + 显示全流程 + 10 个核心坑( OpenGL )
android·flutter·live2d
冬奇Lab8 小时前
ANR实战分析:一次audioserver死锁引发的系统级故障排查
android·性能优化·debug
冬奇Lab8 小时前
Android车机卡顿案例剖析:从Binder耗尽到单例缺失的深度排查
android·性能优化·debug
ZHANG13HAO9 小时前
调用脚本实现 App 自动升级(无需无感、允许进程中断)
android
圆号本昊10 小时前
【2025最新】Flutter 加载显示 Live2D 角色,实战与踩坑全链路分享
android·flutter
小曹要微笑11 小时前
MySQL的TRIM函数
android·数据库·mysql