MySQL索引失效常见场景避免踩坑
建了索引却不走,查询照样全表扫描,是 MySQL 优化里最常见的坑。索引不是建了就一定用,优化器要根据数据分布、写法、类型判断值不值得走。下面把高频失效场景列清楚,配合 explain 验证。
一、先判断走没走索引
EXPLAIN SELECT * FROM user WHERE phone = '13800000000';
看 type 列:const/ref/range 是走了索引;ALL 就是全表扫描。key 列显示实际用到的索引,rows 是预估扫描行数。
二、常见失效场景
1. 对索引列做函数、运算、转换
-- 失效:对列用函数
SELECT * FROM user WHERE LEFT(phone,3) = '138';
-- 失效:算术运算
SELECT * FROM order WHERE id + 1 = 100;
正确写法把运算放到右边:
SELECT * FROM user WHERE phone LIKE '138%';
SELECT * FROM order WHERE id = 99;
2. 隐式类型转换
phone 是 varchar,却传数字,字符串被转成数字导致索引失效:
-- 失效:字符串列传数值
SELECT * FROM user WHERE phone = 13800000000;
-- 正确:加引号
SELECT * FROM user WHERE phone = '13800000000';
3. 复合索引违反最左前缀
索引 (a,b,c),必须从最左列 a 开始用。跳过 a 直接查 b、c 就走不全:
-- 走索引:用到 a
SELECT * FROM t WHERE a = 1;
-- 部分用到:a、b
SELECT * FROM t WHERE a = 1 AND b = 2;
-- 失效:没带最左列 a
SELECT * FROM t WHERE b = 2;
4. 用了 or,且其中一列无索引
-- name 无索引,整个 OR 导致全表扫描
SELECT * FROM user WHERE phone = 'x' OR name = '张三';
5. LIKE 以 % 开头
-- 失效:前导通配符
SELECT * FROM user WHERE name LIKE '%张';
-- 可用:右匹配
SELECT * FROM user WHERE name LIKE '张%';
6. 范围查询后右侧列用不上
复合索引里遇到 >、<、between、like 后,后面的列索引截断。
7. 优化器估算全表更快
表数据量小、或 where 命中比例太高(超过约 20%~30%),优化器认为扫全表比回表划算,主动放弃索引,这属于正常选择。
三、验证与整改
每次改写法都用 explain 复核:
# 看慢查询里有没有全表扫描
grep -E "Rows_examined" slow.log | head
整改思路:索引列保持原样不做函数运算、类型严格匹配、复合索引遵守最左前缀、OR 两侧都建索引或改 UNION。
常见坑
- 以为建了联合索引随便查哪列都行,忽略最左前缀。
- varchar 列忘加引号,隐式转换静默失效。
- 在 where 里写
IFNULL、SUBSTRING、DATE()包裹索引列。 !=、NOT IN常导致放弃索引。- 索引列参与
+ - *计算。 - 数据量小却纠结索引,反而被优化器忽略,属正常。
- 改完不看 explain,凭感觉判断。
总结
索引失效大多来自「在索引列上动手脚」:函数运算、隐式转换、违反最左前缀、前导 %、OR 缺索引。改完写法一定用 explain 的 type 和 key 列验证,确认从 ALL 变成 ref/range 才算修好。
遇到问题加QQ23979811 协助处理