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。

常见坑

  1. 以为建了联合索引随便查哪列都行,忽略最左前缀。
  2. varchar 列忘加引号,隐式转换静默失效。
  3. 在 where 里写 IFNULL、SUBSTRING、DATE() 包裹索引列。
  4. !=、NOT IN 常导致放弃索引。
  5. 索引列参与 + - * 计算。
  6. 数据量小却纠结索引,反而被优化器忽略,属正常。
  7. 改完不看 explain,凭感觉判断。

总结

索引失效大多来自「在索引列上动手脚」:函数运算、隐式转换、违反最左前缀、前导 %、OR 缺索引。改完写法一定用 explain 的 type 和 key 列验证,确认从 ALL 变成 ref/range 才算修好。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-11-15 07:41 
'); })();