WHERE 里用了函数或表达式,索引直接不走
MySQL 的索引存的是字段原始值,一旦你在
WHERE条件里对索引列做计算或调用函数,InnoDB 就没法拿索引树里的值去直接比对了。比如
WHERE YEAR(create_time) = 2023,哪怕
create_time有索引,也白搭。 ❌ 错误写法:
WHERE DATE(order_date) = '2024-04-21'、
WHERE price * 1.1 > 100、
WHERE LEFT(name, 3) = 'Joh'✅ 正确改法:把函数“挪”到右边,转成范围查询——
WHERE order_date >= '2024-04-21' AND order_date 、<code>WHERE price > 100 / 1.1⚠️ 注意:
CAST()或隐式转换(比如字符串字段查数字)本质也是函数,同样失效,别以为只是“少个引号”而已
联合索引没按最左前缀用,后边全作废
复合索引
(a, b, c)是按 B+ 树的层级顺序组织的:先排 a,a 相同再排 b,b 相同再排 c。跳过前面的列,就等于找不到入口,整棵树都用不上。 ❌ 失效场景:
WHERE b = 2 AND c = 3(没 a)、
WHERE a = 1 AND c = 3(跳过 b)、
WHERE a > 10 AND b = 5(a 是范围,b 后续失效) ✅ 解法不是硬凑条件,而是看高频查询反推索引设计:如果常查
city和
district,就建
(city, district),别为了“覆盖更多”硬塞个
(country, city, district)却总只查后两列 ? 补充:范围查询(
BETWEEN、
>、
LIKE 'abc%')会让其后的索引列失效,不是“不能建”,是“建了也用不上”
LIKE 左模糊、OR 混合无索引列、IS NOT NULL 都容易踩坑
这些看着像语法糖的操作,底层其实破坏了索引的有序性或可预估性,优化器一算——不如全表扫快。
❌WHERE name LIKE '%john':B+ 树没法从中间开始找,必须逐行扫 ❌
WHERE a = 1 OR b = 2:如果只有
a有索引、
b没索引,MySQL 往往放弃索引合并,直接全表扫描 ❌
WHERE status IS NOT NULL:B+ 树默认不存
NULL值,查非空就得挨个判断,除非你显式为该列建索引(且字段本身允许
NULL) ✅ 应对:左模糊换全文索引或应用层反向索引;
OR拆成
UNION ALL或给所有涉及列补索引;
IS NOT NULL查询频繁就加
NOT NULL约束,或单独建索引
类型不一致、数据太偏斜,优化器主动弃疗
有时候索引明明存在、写法也没错,但执行计划里
type还是
ALL——不是 bug,是优化器在“理性放弃”。 ❌ 字段是
VARCHAR,却写
WHERE phone = 13800138000:MySQL 自动加
CAST(phone AS UNSIGNED),索引失效 ❌ 某个值占全表 95%,比如
status = 'active',优化器发现走索引还要回表 95% 的记录,不如直接扫聚簇索引 ✅ 验证手段只有一个:
EXPLAIN看
key是否命中、
rows是否合理、
Extra有没有
Using filesort或
Using temporary⚠️ 最容易被忽略的一点:统计信息过期。大批量导入后没跑
ANALYZE TABLE,优化器还按旧分布估算,可能误判索引价值
