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(索引条件下推)。
