对字段使用函数(如YEAR、LOWER)会导致索引失效,因索引基于原始值而非函数结果;应改写为范围查询,如WHERE created_at >= '2023-01-01' AND created_at < '2024-01-01'。

WHERE 条件里对字段做函数操作会直接让索引失效
MySQL 无法使用索引去匹配 YEAR(created_at) 或 LOWER(name) 这类表达式,因为索引是按原始值存储的,不是按函数结果存的。哪怕字段上有 created_at 的 B+ 树索引,WHERE YEAR(created_at) = 2023 也会全表扫描。
- 改写成范围查询:
WHERE created_at >= '2023-01-01' AND created_at - 如果必须用函数,考虑生成列 + 索引(MySQL 5.7+):
ALTER TABLE orders ADD COLUMN created_year INT AS (YEAR(created_at)) STORED,再给created_year加索引 - 注意
LIKE开头带通配符:WHERE name LIKE '%abc'同样走不了索引,要改成WHERE name LIKE 'abc%'才行
隐式类型转换会让索引失效
当 WHERE 条件中字符串和数字比较,或字符集不一致时,MySQL 可能自动转类型,导致索引列被“加工”,从而跳过索引。典型表现是 EXPLAIN 显示 type=ALL 或 key=NULL。
- 常见错误:
WHERE user_id = '123'(user_id是INT类型)→ 改成WHERE user_id = 123 - 跨表 JOIN 时字段字符集不同(如
utf8mb4vsutf8)也会触发转换,检查SHOW CREATE TABLE确认一致性 - 用
CONVERT()或CAST()显式转换时,只要作用在索引列上,同样失效
联合索引最左前缀原则不是“包含就行”,而是“连续匹配”
联合索引 (a, b, c) 只能用于 a、a,b、a,b,c 这三种顺序的等值查询;一旦中间断开(比如只查 b 或 a,c),后面的字段就用不上索引。
-
WHERE a = 1 AND c = 3:只能用上a,c不走索引 -
WHERE b = 2 AND c = 3:完全不走索引 - 范围查询(
>、BETWEEN、LIKE 'xxx%')会截断后续字段,WHERE a = 1 AND b > 10 AND c = 5中c无法命中索引 - 排序也受此约束:
ORDER BY a, b可走索引,但ORDER BY b, c不行
OR 条件容易误伤索引,尤其混用不同字段时
单个 OR 并不必然导致索引失效,但当两边涉及不同字段、或一边无索引时,优化器常放弃使用索引,退化为全表扫描。
-
WHERE a = 1 OR b = 2(a和b都有独立索引)→ MySQL 5.7+ 可能用到index_merge,但不稳定,不如拆成UNION - 更可靠写法:
(SELECT * FROM t WHERE a = 1) UNION ALL (SELECT * FROM t WHERE b = 2 AND a != 1) - 如果其中一边是无索引字段(如
WHERE a = 1 OR status = 'draft',而status无索引),整个条件大概率走全表 - 用
IN替代多个=的OR更安全:WHERE id IN (1,2,3)能走索引
索引是否生效,最终得看 EXPLAIN 的 key 和 rows 字段,别信感觉。很多“明明建了索引却慢”的问题,其实连索引名字都没出现在执行计划里。










