MySQL中OR查询索引未生效,通常因未满足Index Merge前提:需a、b各自有单列索引(非联合索引),且无函数、表达式或隐式转换;EXPLAIN中出现Using union(…)才表示生效,否则应改用UNION ALL或单列索引优化。

MySQL 用 OR 时索引没生效?先确认是否触发了 Index Merge
MySQL 对 OR 条件的处理很“挑”:不是所有带 OR 的查询都能走索引,更不是所有能走索引的都会合并。真正起作用的是「Index Merge」优化策略,但它有硬性前提——每个 OR 分支必须能独立走**单列索引**(不能是联合索引的一部分被部分使用),且这些索引字段之间不能有函数、表达式或类型隐式转换。
常见错误现象:EXPLAIN 显示 type=ALL 或 key=NULL,但明明字段上有索引;或者 Extra 列出现 Using union(...) —— 这才是 Index Merge 成功的标志。
- 检查方法:执行
EXPLAIN SELECT ... WHERE a=1 OR b=2,看key和Extra字段 - 必须满足:
a和b各自都有单列索引(如INDEX(a)和INDEX(b)),不能只有INDEX(a,b) - 如果用了
WHERE a+1=2 OR b=3,Index Merge 直接失效——表达式会让索引失效
为什么联合索引对 OR 基本无效
联合索引 INDEX(a,b) 只能高效支持最左前缀匹配,比如 a=1 或 a=1 AND b=2。但 a=1 OR b=2 中,b=2 部分无法利用该索引的 B+Tree 结构——因为 b 不是最左列,数据在物理上不是按 b 排序的。
这时候 MySQL 不会“拆开”联合索引去扫描 b 的值,也不会回表后过滤——它要么放弃索引全表扫,要么退而求其次尝试 Index Merge(前提是存在独立的 INDEX(b))。
- 典型陷阱:给
(user_id, status)建联合索引,想靠它加速WHERE user_id=123 OR status='active'—— 实际几乎不走索引 - 正确做法:删掉无意义的联合索引,改为建两个单列索引
INDEX(user_id)和INDEX(status) - 注意:如果
status区分度极低(比如只有 2–3 个值),MySQL 可能仍拒绝用它的索引——优化器认为全表扫描更快
OR 查询性能差?优先考虑 UNION ALL 替代
当 Index Merge 不生效、或生效但性能仍差(比如多个索引扫描后合并结果集开销大),UNION ALL 往往更可控、更可预测。它强制把逻辑拆成独立子查询,每个都能走自己的索引,且避免了优化器“猜错”执行计划的风险。
示例:SELECT * FROM orders WHERE user_id = 100 OR status = 'shipped' 改写为:
SELECT * FROM orders WHERE user_id = 100 UNION ALL SELECT * FROM orders WHERE status = 'shipped' AND user_id != 100
关键点在于第二句加了 AND user_id != 100 去重(如果业务允许重复,可省略;否则用 UNION,但会多一次排序去重)。
- 优势:每个子句可独立命中索引,
EXPLAIN清晰可见,不受优化器对 OR 的启发式限制影响 - 代价:需要人工保证语义等价,特别是 NULL 处理和去重逻辑
- 注意:如果两个条件有大量交集,
UNION ALL返回行数可能比原OR多——得看业务是否容忍
哪些情况 Index Merge 反而让查询变慢
Index Merge 不是银弹。当涉及的索引太多、或单个索引返回结果集太大时,合并操作本身(临时内存排序、去重、堆合并)开销可能超过全表扫描。尤其在老版本 MySQL(5.6 之前)或小表上,这种现象更明显。
典型信号:EXPLAIN 显示 Using union(a,b); Using where,但实际执行时间远超预期;SHOW PROFILE 发现 Creating sort index 或 Copying to tmp table 占比高。
- 规避方式:用
FORCE INDEX强制走一个高区分度字段的索引,再在应用层做二次过滤 - 或者改写为
IN(如果OR是等值判断且字段类型一致):WHERE user_id IN (100, 200, 300)比user_id=100 OR user_id=200 OR user_id=300更易走索引 - 最实在的一招:对高频
OR查询场景,直接建覆盖索引,减少回表——哪怕不能合并,至少单路索引扫描更快
Index Merge 看似自动聪明,实则边界极窄。它依赖精确的索引结构、干净的查询写法、以及优化器对成本的估算——而这三者在真实业务中常常不满足。与其赌它生效,不如从建索引习惯开始:少建“看起来全面”的联合索引,多留几个轻量单列索引,再配合 UNION ALL 手动控制执行路径。










