explain显示type=all且key=null说明优化器未使用索引,常见原因包括:索引列被函数或表达式包裹、违反最左前缀原则、隐式类型转换、或优化器评估全表扫描成本更低。

EXPLAIN 显示 type=ALL 且 key=NULL,基本就能断定索引没被用上——不是索引“坏了”,而是 MySQL 优化器主动绕开了它。
WHERE 条件里对索引列用了函数或表达式
这是最常见也最容易踩的坑。索引是按原始值排序的,一旦你把索引列塞进函数里,MySQL 就没法直接定位。
-
DATE(create_time) = '2023-01-01'→ 失效;改用create_time >= '2023-01-01' AND create_time -
UPPER(name) = 'JOHN'→ 失效;改用name = 'john'(配合小写索引)或建函数索引(MySQL 8.0+) -
price * 1.1 > 100→ 失效;改写为price > 100 / 1.1
核心原则:索引列 必须“裸露”出现在 WHERE 左侧,不能被任何运算、函数、类型转换包裹。
联合索引没走最左前缀
组合索引 (a, b, c) 实际等价于三个索引:(a)、(a,b)、(a,b,c)。跳过最左列,整条索引就废了。
-
WHERE b = 2 AND c = 3→ 完全不走索引 -
WHERE a = 1 AND c = 3→ 只能用上a,c不生效(中间断档) -
WHERE a = 1 AND b > 10 AND c = 5→c生效不了(范围查询后列失效)
设计时要把高频等值查询字段放最左;区分度高的字段优先前置(比如 COUNT(DISTINCT col)/COUNT(*) 接近 1 的更适合放前面)。
隐式类型转换让索引“隐形”
字段类型和传入值类型不一致时,MySQL 会悄悄做转换,而这个过程发生在每行数据上,索引就失效了。
-
user_id是VARCHAR,但写成WHERE user_id = 123→ 转成字符串比较,索引失效 -
phone是VARCHAR,但写成WHERE phone = 13800138000→ 转成数字,索引失效 - DATETIME 字段用字符串
'2023-01-01'比较,若字符集或格式不匹配,也可能触发转换
解决方法很简单:查什么类型,就传什么类型。加引号、补时分秒、对齐字符集。开启 sql_mode=STRICT_TRANS_TABLES 能提前暴露这类问题。
优化器评估后觉得全表扫描更便宜
哪怕条件完全合规,索引也可能被放弃——尤其当表很小(几百行)、结果集很大(>30% 行数)、或回表代价太高时。
-
EXPLAIN看到rows很大、filtered很低,说明命中率差 -
ANALYZE TABLE table_name更新统计信息,有时能扭转优化器决策 - 强制索引(
FORCE INDEX)风险高:统计不准或数据分布变化后,反而更慢
真正难缠的是这种“看起来该走却没走”的情况——它不报错、不警告,只默默拖慢接口,必须靠 EXPLAIN 主动盯住。











