索引未生效主因是where中对索引列使用函数或运算、隐式类型转换、子查询相关性导致dependent subquery、统计信息失真及like前导通配符等,需通过explain分析type、key、rows并针对性优化。

有索引但子查询依然慢,不是索引没建,而是索引没被用上,或者用了但执行路径不合理。关键得看 EXPLAIN 里有没有 DEPENDENT SUBQUERY、type 是不是 ALL 或 index、rows 预估是否严重失真。
为什么加了索引,EXPLAIN 还显示 type=ALL 或 DEPENDENT SUBQUERY
索引存在 ≠ 索引生效。常见原因:
-
WHERE条件中对索引字段用了函数或运算,比如WHERE DATE(create_time) = '2026-08-01'或WHERE id + 1 = 100,MySQL 无法走索引 - 子查询里引用了外层字段(如
WHERE t2.id = t1.ref_id),触发相关子查询,优化器无法物化结果,每行主表都重跑一次子查询 - 子查询本身带
GROUP BY、LIMIT、HAVING或UNION,MySQL 放弃优化,强制回退为嵌套循环 - 索引字段类型和查询值类型不匹配,比如
id是INT,但写成WHERE id = '123',触发隐式转换,索引失效
子查询走了索引,但 rows 预估数远高于实际返回行数
这说明统计信息过期,优化器误判了数据分布,导致选错执行策略。例如预估扫 50 万行,实际只返回 3 行,它可能放弃使用索引而选全表扫描,或错误选择驱动表顺序。
- 运行
ANALYZE TABLE table_name更新统计信息(MySQL 8.0+ 默认自动,但大表或批量导入后仍建议手动) - 检查
information_schema.STATISTICS中索引基数(CARDINALITY)是否明显偏离真实值 - 若表频繁写入,考虑关闭
innodb_stats_auto_recalc并定期ANALYZE,避免统计抖动
LIKE '%xxx' 或 NOT IN 导致索引完全失效
前导通配符让 B+ 树索引失去有序性优势;NOT IN 则因 NULL 值语义问题,MySQL 往往放弃索引下推,改用临时表 + 全量比对。
-
LIKE 'd.f.%'可走索引,LIKE '%d.f.'不行——优先把模糊条件转为前缀匹配,或用LEFT(username, 3) = 'd.f' -
NOT IN (SELECT ...)建议一律换成NOT EXISTS,避免 NULL 导致整个条件为UNKNOWN,查不到数据 - 若子查询结果固定且复用频繁,直接建临时表:
CREATE TEMPORARY TABLE tmp_ids AS SELECT DISTINCT id FROM ...,再加主键索引
IN 子查询改写为 EXISTS 后反而更慢
这不是语法问题,是语义和执行路径没对齐。EXISTS 有效必须满足三个硬条件:子查询能走索引、只判断存在性、关联条件明确。
- 原写法
WHERE o.customer_id IN (SELECT c.id FROM customers c WHERE c.country = 'CN')缺少c.id = o.customer_id关联,改写后变成笛卡尔积 - 子查询里
SELECT *或SELECT id没改成SELECT 1,部分 MySQL 版本会多解析字段,增加开销 - 主表极大(千万级)、子查询结果极小(几十行),此时
IN可能被优化器转为哈希查找,比EXISTS的嵌套循环更快
最容易被忽略的是:改完 SQL 没清缓存、没看新 EXPLAIN、也没验证 NULL 行是否逻辑一致。索引只是工具,真正起作用的是执行计划里那条路径是否被真正采纳。










