MySQL索引失效主因有三:WHERE中对字段用函数或表达式(如YEAR(create_time))、复合索引中范围查询后列无法命中、统计信息过期或数据倾斜致优化器误判;需改写为范围条件、定期ANALYZE TABLE并警惕隐式转换。

WHERE 条件用了函数或表达式导致 index 失效
MySQL 无法对计算后的结果使用索引,哪怕字段本身有索引。比如 WHERE YEAR(create_time) = 2023,create_time 上有索引也没用——因为优化器要先算出每行的 YEAR(),再比对,没法走 B+ 树的有序查找。
常见场景:日期按年/月查询、字符串 UPPER()/LOWER() 比较、CONCAT() 拼接后匹配。
- 改写为范围查询:
WHERE create_time >= '2023-01-01' AND create_time - 避免在索引列上做任何运算,包括隐式类型转换(如
WHERE user_id = '123'而user_id是INT) - 真需要函数检索,考虑生成列 + 函数索引(MySQL 5.7+ 支持
STORED列,8.0+ 支持函数索引)
LIKE 查询以通配符开头触发全表扫描
LIKE '%abc' 或 LIKE '%abc%' 会让索引失效,因为 B+ 树是按前缀有序组织的,没法从中间或结尾反向定位。
只有 LIKE 'abc%' 这种左前缀模式才能用上索引(前提是该字段是联合索引最左列或独立索引)。
- 模糊搜索需求强烈时,别硬扛,考虑
FULLTEXT索引或外部搜索引擎(如 Elasticsearch) - 如果只是查“是否包含某子串”,且数据量不大,
INSTR()或正则可能更直白,但同样不走索引 - 注意
LIKE的 ESCAPE 字符设置错误也会导致计划异常,可用EXPLAIN验证key字段是否为空
联合索引没遵循最左前缀原则
联合索引 (a, b, c) 实际上只建立了三个有效索引路径:a、(a,b)、(a,b,c)。跳过 a 直接查 b 或 (b,c),索引就完全失效。
典型错误:WHERE b = 2 AND c = 3,即使 (a,b,c) 有索引,也扫全表。
- 写 WHERE 条件时,把高选择性、常用于过滤的字段尽量放联合索引左边
- ORDER BY 和 GROUP BY 字段若想复用索引,必须和联合索引前缀严格一致(顺序、方向、无断层)
- 存在
WHERE a = 1 AND b > 10 AND c = 5这类混合等值+范围时,c通常无法继续走索引(范围之后的列失效),需评估是否拆分索引
统计信息过期或数据分布倾斜让优化器误判
MySQL 依赖表的统计信息(如 cardinality)估算成本,决定是否走索引。当大量 INSERT/DELETE 后未更新统计,或某值占比极高(如 status = 'active' 占 95%),优化器可能认为走索引比全表扫描还慢,主动放弃。
现象:明明有索引,EXPLAIN 显示 type: ALL,key: NULL。
- 手动更新统计:
ANALYZE TABLE table_name(轻量,推荐定期执行) - 强制走索引仅作验证:
SELECT * FROM t USE INDEX (idx_a_b) WHERE ...,但生产环境慎用 - 检查
SHOW INDEX FROM table_name中的Cardinality是否明显失真;超大表可调大innodb_stats_persistent_sample_pages
索引失效往往不是“建了没用”,而是查询写法、数据特征和优化器判断三者共同作用的结果。最容易被忽略的是隐式类型转换和统计信息滞后——这两点连 EXPLAIN 都不一定一眼看出问题。


















