索引可能失效,主因包括违反最左前缀原则、隐式类型转换、在索引列使用函数或运算、统计信息过期等;需通过EXPLAIN验证执行计划,确保type非ALL、key非NULL,并合理设计查询与索引。

索引不是建了就一定生效,很多查询看似走了索引,实则执行计划里显示 type=ALL 或 key=NULL,本质是索引失效。核心原因在于查询条件没匹配索引的最左前缀、数据类型隐式转换、函数/运算干扰、或统计信息过期等。
最左前缀原则被破坏
复合索引(如 INDEX idx_name_age_city (name, age, city))要求查询条件从左开始连续使用字段。如果只查 age 或 city,或跳过 name 直接用 name AND city(中间缺 age),索引就无法有效下推。
- ✅ 正确:WHERE name = '张三';WHERE name = '张三' AND age > 25;WHERE name = '张三' AND age = 28 AND city = '北京'
- ❌ 失效:WHERE age = 25;WHERE city = '北京';WHERE name = '张三' AND city = '北京'(缺少 age 条件)
隐式类型转换导致索引失效
当查询字段与条件值类型不一致,MySQL 会自动做隐式转换。比如字段是 VARCHAR,但传入数字(WHERE mobile = 13800138000),MySQL 可能将整列转为数字比对,放弃索引。
- 检查执行计划中 type 是否为 ALL 或 key 显示 NULL
- 用 EXPLAIN FORMAT=TRADITIONAL 查看 Extra 列是否出现 Using where; Using index(正常)还是只有 Using where(可能失效)
- 统一参数类型:字符串条件务必加引号,避免写成 WHERE status = 1(status 是 ENUM 或 VARCHAR)
在索引列上使用函数或表达式
哪怕只是加个 UPPER()、DATE()、+、-,都会让索引失效,因为 B+ 树存的是原始值,无法直接匹配计算后结果。
- ❌ 失效:WHERE UPPER(name) = 'LISA';WHERE YEAR(create_time) = 2023;WHERE price * 1.1 > 100
- ✅ 替代方案:
• 函数查询 → 改为生成列 + 索引(MySQL 5.7+):ALTER TABLE t ADD name_upper VARCHAR(50) STORED AS (UPPER(name)); CREATE INDEX idx_name_upper ON t(name_upper);
• 时间范围 → 用区间代替函数:WHERE create_time >= '2023-01-01' AND create_time
• 运算条件 → 移项处理:WHERE price > 100 / 1.1
统计信息不准或索引选择失衡
MySQL 依赖表的统计信息(如索引基数 cardinality)决定是否走索引。若数据批量导入后未更新统计信息,或索引区分度极低(如 sex 字段只有 'M'/'F'),优化器可能误判全表扫描更快。
- 手动更新统计:执行 ANALYZE TABLE table_name;
- 强制指定索引(慎用):SELECT * FROM t USE INDEX (idx_name_age) WHERE ...
- 评估索引价值:用 SHOW INDEX FROM t; 查 Cardinality,若远小于表总行数且字段值重复率高,该索引收益有限
不复杂但容易忽略。真正有效的索引,得让查询条件“严丝合缝”地贴合索引结构,同时避开类型和计算陷阱。每次上线新查询,记得看一眼 EXPLAIN 输出,比盲目加索引管用得多。










