索引未被使用主因有四:where中对索引列用函数或表达式、隐式类型转换、联合索引未遵循最左前缀原则、低选择性或优化器认为全表扫描更优。

EXPLAIN 显示 key 为 NULL 或 type 是 ALL,说明索引根本没被选中——不是 MySQL “不认”,而是你的查询写法、数据特征或索引结构本身不满足使用条件。
WHERE 中对索引列用了函数或表达式
这是最隐蔽也最高频的失效点。只要索引列被包裹在函数里,B+ 树就无法直接定位。
-
YEAR(created_at) = 2023→ 即使created_at有索引,也会全表扫描 - 正确写法是改用范围:
created_at >= '2023-01-01' AND created_at - MySQL 8.0+ 可建函数索引补救:
CREATE INDEX idx_year ON orders ((YEAR(created_at))),但该索引只对YEAR()查询有效,不能复用到其他条件
隐式类型转换让索引“自动失效”
字段是 VARCHAR,你传入数字;字段是 INT,你传入字符串——MySQL 会悄悄把索引列转成另一类型,导致索引跳过。
- 典型例子:
phone VARCHAR(20)有索引,但写成WHERE phone = 13800001111→ 触发隐式转换,索引失效 - 验证方法:
SHOW WARNINGS看是否有Truncated incorrect DOUBLE value类警告 - 修复原则:值类型必须和字段类型严格一致;宁可用
CAST('13800001111' AS CHAR)显式转,也不依赖隐式转换
联合索引没按最左前缀匹配,后面全作废
复合索引 (a, b, c) 是一棵按 a → b → c 排序的树,跳过前面,后面不可见。
- 能命中:
WHERE a = 1、WHERE a = 1 AND b = 2、WHERE a = 1 AND b = 2 AND c > 3 - 不能命中:
WHERE b = 2、WHERE c = 3、WHERE b = 2 AND c = 3(a缺失) - 注意范围查询中断后续列:
WHERE a = 1 AND b > 2 AND c = 3中,c实际无法走索引(b > 2是区间,c在该区间内无序)
低选择性或优化器判定全表扫描更快
索引不是万能加速器。当字段重复值太多(比如 state CHAR(2) 只有 50 个值),或表很小(几千行),优化器可能直接放弃索引。
- 检查方式:
SELECT COUNT(DISTINCT state) / COUNT(*) FROM people;,结果越接近 0,选择性越差 - 强制走索引仅作诊断:
SELECT * FROM people WHERE state = 'CA' FORCE INDEX (state);,但生产环境慎用 - 真正有效的解法是结合业务:比如加过滤条件缩小范围,或改用覆盖索引(如
INDEX(state, id, name))减少回表
真正容易被忽略的是:索引是否“被考虑”和是否“被选用”是两步。possible_keys 非空但 key 为 NULL,说明优化器评估后主动放弃了它——这时候看 rows 和 filtered 比纠结“为什么没用”更有价值。











