mysql优化器对or查询不支持部分走索引,若任一列无索引则倾向全表扫描;即使均有索引,也仅在满足等值查询、索引类型兼容且成本更低时启用索引合并;推荐用union all替代or或为各列单独建索引。

OR条件中只要有一个列没索引,优化器大概率放弃走索引
MySQL优化器面对 WHERE a = 1 OR b = 2 这类查询时,不会“部分使用”索引。它得保证能用索引快速定位所有满足任一条件的行。如果 b 没索引,那哪怕 a 有主键或二级索引,优化器也倾向于直接全表扫描——因为扫一遍比“先走索引查a,再回表或全扫查b,最后去重合并”更省事。
常见错误现象:
-
EXPLAIN显示type: ALL,key: NULL - 明明
WHERE a = 1单独执行很快,加上OR b = 2后响应时间飙升数倍
这不是bug,是成本估算结果。优化器认为:索引扫描 + 非索引列扫描 + 去重 = 更高开销。
即使所有OR列都有索引,也不保证走索引
有索引只是必要条件,不是充分条件。MySQL 5.6+ 引入了“索引合并(Index Merge)”优化,但默认只在特定条件下启用:
- 每个OR分支必须是独立的等值查询(
=、IN),不能是范围(>、BETWEEN)或函数表达式 - 各列索引类型需兼容(比如都是B+树,不能混哈希)
- 优化器评估后认为合并成本低于全表扫描——数据量小、选择性差时仍可能跳过
实操建议:
- 用
EXPLAIN FORMAT=JSON查看query_plan中是否出现"using_index_merge" - 不要依赖它;主动建复合索引或改写查询更可控
UNION ALL 替代 OR 是最稳妥的落地方案
把 WHERE a = 1 OR b = 2 拆成两个独立查询,明确告诉优化器“每个分支都可走索引”:
SELECT * FROM t WHERE a = 1 UNION ALL SELECT * FROM t WHERE b = 2 AND a != 1;
注意点:
- 用
UNION ALL而非UNION,避免隐式去重开销 - 第二句加
AND a != 1是为避免重复(若业务允许重复,可省略) - 确保
a和b各自有索引,否则拆了也没用
这个写法强制每个子查询走各自索引,执行计划清晰、性能可预期。
复合索引对OR的收益有限,别乱建
有人想“既然OR涉及a和b,那就建 (a, b) 复合索引”,这通常无效。复合索引用于 AND 场景才发挥最左前缀优势;对 OR 来说,(a, b) 索引无法加速 WHERE b = 2 单独条件(违反最左前缀)。
真正有效的做法是:
- 为
a单独建索引,为b单独建索引(最直接) - 如果查询固定且高频,考虑覆盖索引减少回表,例如
INDEX idx_a (a, c, d)+INDEX idx_b (b, c, d) - 避免为OR场景建冗余复合索引,徒增写开销和维护负担
索引失效常发生在“以为建了就万事大吉”的地方——OR本身不触发最左前缀规则,这点容易被忽略。











