mysql执行计划是如何生成的_mysql执行计划解析

来源:这里教程网 时间:2026-02-28 20:47:46 作者:

MySQL 为什么有时
EXPLAIN
显示的执行计划和实际执行不一致

因为

EXPLAIN
默认只做「静态分析」:它不真正执行 SQL,而是基于统计信息、索引结构、查询条件等估算成本,选择它认为最优的访问路径。但估算依赖的行数(
rows
)、索引基数(
cardinality
)、数据分布是否倾斜,都可能过期或不准。

常见诱因包括:

ANALYZE TABLE
没有及时更新,导致优化器误判某索引“更高效”
WHERE 条件含函数(如
WHERE YEAR(create_time) = 2023
),索引失效,但
EXPLAIN
可能仍显示
key
非空(实为回退到全表扫描)
使用了
UNION
或子查询,
EXPLAIN
对各分支单独估算,无法反映嵌套执行时的中间结果集大小变化
开启了
optimizer_switch='index_merge=on'
等高级特性,但实际运行中因锁竞争或 buffer pool 压力导致走不同路径

type
字段值从
const
ALL
的真实含义差异

type
描述的是 MySQL 如何定位数据行,不是“快慢等级表”,而是「访问方式的本质区别」。关键不在名字,而在它背后触发的 I/O 行为:

const
:主键/唯一索引等值匹配,且优化器确认最多一行 → 直接定位磁盘页,无需循环
ref
:非唯一索引等值查找 → 定位索引 B+ 树叶子页后,需遍历可能的多条记录
range
:索引范围扫描 → B+ 树中做区间遍历,但不回表(若覆盖索引)或需回表(若未覆盖)
index
:全索引扫描 → 遍历整个索引 B+ 树叶子链表,比
ALL
快仅因不用读聚簇索引数据页
ALL
:全表扫描 → 逐行读聚簇索引数据页,最耗 I/O;但若
SELECT *
且表很小,可能比多次随机回表更快

注意:

type=index
不代表用了索引就一定好——如果索引字段少、回表代价高,而聚簇索引本身很紧凑,
ALL
反而更优。

如何让
EXPLAIN FORMAT=TREE
真正有用

FORMAT=TREE
输出的是优化器最终决策的执行树结构,比传统格式更能暴露「为什么选这个连接顺序」「聚合下推是否发生」。但它默认关闭,且依赖 MySQL 8.0.16+,启用前必须确认:

执行
SET optimizer_switch='use_index_extensions=on,condition_fanout_filter=on';
否则部分优化细节被隐藏
避免在
EXPLAIN
中混用用户变量(如
@var:=@var+1
),会导致
FORMAT=TREE
降级为传统格式
关注输出中的
->
缩进层级:最深的节点是实际最先执行的物理操作(如
Index lookup
),而非 SQL 书写的顺序
若看到
Using temporary; Using filesort
出现在子节点中,说明该层已触发内存/磁盘临时表,不是外层问题

key_len
值比预期小,是不是索引没用上

不一定。

key_len
显示的是 MySQL 实际使用的索引字节数,受字段类型、是否允许 NULL、字符集影响极大。例如:

VARCHAR(255)
字段建了联合索引
(a,b,c)
,但
WHERE a='x' AND b IS NULL
key_len
只算
a
长度,
b
因为
IS NULL
不参与索引查找
utf8mb4
VARCHAR(10)
最大占 40 字节(10×4),但若实际值全是 ASCII,
key_len
可能只显示 10
TINYINT NOT NULL
占 1 字节,但加了
DEFAULT NULL
后,
key_len
多出 1 字节用于存储 NULL 标志位

真正判断索引是否生效,应结合

key
(是否非 NULL)、
rows
(预估扫描行数是否合理)、以及
Extra
中是否出现
Using index
(覆盖索引)或
Using where
(索引条件下推)。

相关推荐