索引失效 = MySQL 优化器决定不用你建的索引去执行。 永远用 EXPLAIN 验证,不要靠记忆。
12 种失效场景
1. WHERE 列上做函数 / 表达式
1 2 3 4 5 6 7
-- ❌ 索引失效 SELECT*FROM users WHEREYEAR(created_at) =2024; SELECT*FROM users WHERE id +1=100;
-- ✅ 改写:把函数挪到右边 SELECT*FROM users WHERE created_at >='2024-01-01'AND created_at <'2025-01-01'; SELECT*FROM users WHERE id =99;
MySQL 8.0+ 支持 函数索引:ALTER TABLE users ADD INDEX idx_year ((YEAR(created_at)))
2. 隐式类型转换
1 2 3 4 5 6
-- 字段是 VARCHAR,传了 INT -- ❌ 走全表扫 SELECT*FROM users WHERE phone =13800138000;
-- ✅ SELECT*FROM users WHERE phone ='13800138000';
3. LIKE 以 % 开头
1 2 3 4 5 6 7
-- ❌ SELECT*FROM users WHERE name LIKE'%张';
-- ✅ 前缀匹配能走索引 SELECT*FROM users WHERE name LIKE'张%';
-- 想中间匹配?用全文索引或 ES
4. OR 中有非索引列
1 2 3 4 5 6 7
-- ❌ phone 没索引,整条 SQL 失效 SELECT*FROM users WHERE id =1OR phone ='13800138000';
-- ✅ UNION ALL 拆开 SELECT*FROM users WHERE id =1 UNIONALL SELECT*FROM users WHERE phone ='13800138000'AND id !=1;
5. 联合索引违反最左前缀
1 2 3 4 5 6
-- INDEX (a, b, c) WHERE a =1-- ✅ WHERE a =1AND b =2-- ✅ WHERE a =1AND c =3-- ✅ 但 c 用不上 WHERE b =2AND c =3-- ❌ WHERE c =3-- ❌
6. 范围查询后的列失效
1 2 3
-- INDEX (a, b, c) WHERE a =1AND b >10AND c =3 -- ✅ a, b 走索引,c 用不上(b 是范围)
7. 负向查询
1 2 3 4 5
-- ❌ NOT IN / != / NOT LIKE 通常走全表 SELECT*FROM users WHERE status !=1;
-- ✅ 改正向 SELECT*FROM users WHERE status IN (2, 3, 4);
8. IS NULL / IS NOT NULL(看版本和数据)
1 2 3 4
-- 看选择性 -- 大部分都是 NULL → IS NOT NULL 走全表 -- 少部分是 NULL → IS NULL 可以用索引 SELECT*FROM users WHERE deleted_at ISNULL;
9. ORDER BY 方向不一致
1 2 3 4 5
-- INDEX (a, b) ORDERBY a ASC, b ASC-- ✅ ORDERBY a DESC, b DESC-- ✅(MySQL 8 之前是反向扫) ORDERBY a ASC, b DESC-- ❌ 8 之前会 filesort -- 8.0+ 支持 INDEX (a ASC, b DESC) 显式声明