MySQL索引可能因函数/表达式、隐式类型转换、LIKE前缀通配、NOT/!=/OR等操作而失效,需通过EXPLAIN验证并遵循裸列查询、统一类型、避免前导%等原则。
MySQL 索引不是建了就一定生效的——优化器会根据成本(cost)决定是否走索引,而很多看似合理的写法,实际会让索引完全失效。下面这几种情况,在日常 SQL 编写和线上慢查排查中高频出现,务必警惕。
比如 DATE(create_time) = '2025-12-31'、SUBSTR(phone, 1, 3) = '138'、price * 1.1 > 100,这些都会让索引失效。
SELECT * FROM orders WHERE create_time >= '2025-12-31 00:00:00' AND create_time < '2025-01-01 00:00:00';
ADD COLUMN create_date DATE AS (DATE(create_time)) STORED,再给 create_date 加索引联合索引 KEY idx_name_age_city (name, age, city),以下查询会失效:
WHERE age = 25(跳过 name,直接查中间列)WHERE city = '北京'(只用最右列)WHERE name = '张三' AND city = '上海'(跳过 age,中断连续性)✅ 能走索引的写法必须从左开始连续使用:

WHERE name = '张三' ✔️
WHERE name = '张三' AND age > 20 ✔️
WHERE name = '张三' AND age = 25 AND city LIKE '上%' ✔️(注意:LIKE 后缀带 % 仍可用索引)
字段定义为 VARCHAR,但查询时传了数字:WHERE user_id = 12345 —— 看似没问题,实则触发隐式转换,等价于 WHERE CAST(user_id AS SIGNED) = 12345,索引失效。
WHERE user_id = '12345'
age = '25'),但这不可靠,取决于数据分布和 MySQL 版本,**一律应避免混用类型**EXPLAIN 看 type 是否为 ALL 或 key 是否为 NULL
WHERE name LIKE '%三'、WHERE status != 1、WHERE name = '张三' OR age = 25,这些都极大概率触发全表扫描。
LIKE '%xxx':无法利用 B+ 树的有序性,只能扫全表;若必须模糊前缀,考虑全文索引或 Elasticsearch!= 或 NOT IN:优化器通常认为“排除少数”不如“扫描多数再过滤”,尤其当结果集占比高时OR:只要任意一个分支字段无索引,整个 WHERE 就可能放弃索引(即使另一侧有);拆成 UNION ALL 是常见绕过方式真正容易被忽略的一点:索引是否生效,不只看语法,更要看数据分布和优化器估算。比如一张只有 100 行的表,哪怕写了 WHERE id = ?,MySQL 也可能直接全表扫描——因为比走索引还快。所以别只信“我加了索引”,要信 EXPLAIN 的输出和真实执行时间。