为什么 EXPLAIN
显示 type=ALL
却没走索引
这通常不是索引没建,而是查询写法触发了隐式类型转换或函数包裹,导致索引失效。比如
WHERE id = '123'(
id是
INT),MySQL 会把字符串转成数字再比对,但优化器可能放弃索引;更常见的是
WHERE DATE(create_time) = '2024-01-01'——
DATE()函数让
create_time索引完全失效。
实操建议:
用SHOW WARNINGS查看优化器重写后的语句,确认是否被改写成不可索引的形式 避免在索引列上使用函数、表达式、
LIKE '%xxx'开头的模糊匹配 字符串比较时确保类型一致:
WHERE status = 1(
status是
TINYINT),别写成
'1'时间范围查询优先用
BETWEEN或闭区间:
WHERE create_time >= '2024-01-01' AND create_time ,而非 <code>DATE(create_time) = ...
EXPLAIN FORMAT=JSON
比传统 EXPLAIN
多看出什么
传统
EXPLAIN只给粗粒度信息,比如
key用了哪个索引、
rows预估扫描行数;而
FORMAT=JSON能暴露优化器真实决策路径,特别是是否发生索引合并(
index_merge)、是否使用了索引条件下推(ICP)、是否因
ORDER BY需要临时文件排序等。
关键字段注意:
attached_condition:显示下推到存储引擎层的过滤条件,ICP 生效时这里会变短
using_index:为
True表示覆盖索引,无需回表
using_filesort或
using_temporary:说明排序/分组没走索引,性能瓶颈大概率在这
filtered值过低(如
10.00)说明虽然走了索引,但索引选择性差,大量无效行被拉到 server 层过滤
哪些 ORDER BY
场景必须重写才能走索引
只有当
ORDER BY字段顺序与索引最左前缀严格一致、且无混合
ASC/DESC时,才可能利用索引排序。例如有联合索引
(a, b, c),则
ORDER BY a, b可走索引,但
ORDER BY b, c不行;
ORDER BY a DESC, b ASC在 MySQL 8.0 之前也基本失效。
常见可重写方式:
把ORDER BY RAND()改为应用层随机取 ID 后查,或用
OFFSET+
LIMIT分页模拟(但大数据量仍慢)
ORDER BY func(x)(如
UPPER(name))无法走索引,考虑加函数索引(MySQL 8.0+):
CREATE INDEX idx_name_upper ON t ((UPPER(name)))多表
JOIN后
ORDER BY非驱动表字段,往往触发
using_filesort,优先调整
JOIN顺序或加覆盖索引包含排序字段
查询重写时容易忽略的统计信息偏差问题
即使重写后语法“看起来能走索引”,如果表的统计信息陈旧(比如刚批量导入百万数据但没
ANALYZE TABLE),优化器仍可能基于错误的行数估算选择全表扫描。尤其在
WHERE条件区分度高但优化器误判为低时,
EXPLAIN会显示
type=range却实际扫描巨量行。
验证和修复步骤:
查统计信息更新时间:SELECT UPDATE_TIME FROM information_schema.tables WHERE TABLE_SCHEMA = 'db' AND TABLE_NAME = 't'强制刷新:
ANALYZE TABLE t(线上慎用,会锁表;MySQL 5.7+ 可设
innodb_stats_auto_recalc = ON) 必要时用
FORCE INDEX临时绕过错误选择,但只是兜底,不能替代根本优化 注意
innodb_stats_persistent是否开启,关闭时重启后统计信息丢失,可能导致计划突变
真正难的不是写出能走索引的 SQL,而是理解优化器在特定数据分布、统计精度、版本特性下的真实行为边界。一次
EXPLAIN看不出问题,得结合
FORMAT=JSON、执行时间、
SHOW PROFILE和实际
slow log对齐才可靠。
