or条件导致复合索引失效,因其破坏最左前缀原则:如索引(a,b),where a=1 or b=2中b=2跳过最左列a,无法定位b+树起始位置,优化器被迫全表扫描。

OR条件为什么不能用复合索引的最左前缀
复合索引 (a, b) 的结构决定了它只对满足“从左开始连续匹配”的查询有效。而 WHERE a = 1 OR b = 2 中,b = 2 完全跳过了最左列 a,索引树无法定位起始位置——就像查电话簿,你不说姓氏(a),只说名字叫“小明”(b = 2),系统没法快速翻到那一页。
即使 a 和 b 都有单列索引,优化器也未必走 index_merge;但只要涉及 OR 且任一条件不满足最左前缀,(a, b) 这个复合索引就彻底失效,EXPLAIN 中 key 会是 NULL,type 变成 ALL。
等值+OR+复合索引,什么情况下看似能用实则白搭
有人试过建 (a, b) 索引后写 WHERE a = 1 OR a = 2,发现走了索引——但这不是因为 OR 被优化了,而是优化器把它重写成了 IN(a IN (1, 2)),本质还是单字段等值查询。真正危险的是跨字段组合:
-
WHERE a = 1 OR b = 2:b = 2不满足最左前缀,(a, b)无用 -
WHERE a = 1 OR b > 10:范围条件b > 10后面的列本就不能用,但这里连a的等值部分都救不了整条OR -
WHERE a = 1 OR (a = 2 AND b = 5):括号没用,优化器仍按整体逻辑估算,大概率弃索引
为什么FORCE INDEX或提示也很难强制复合索引生效
你加了 FORCE INDEX (idx_a_b),但执行计划里 key 还是空?因为 FORCE INDEX 只能指定“用哪个索引”,不能改变“怎么用”。而 OR 条件中 b = 2 这一分支根本不符合 (a, b) 的扫描逻辑——没有 a 值,就无法在 B+ 树里定位任何数据页,强制也没意义。
此时优化器要么报错(某些版本),要么直接忽略提示,回落到全表扫描。这不是配置问题,是存储结构决定的硬限制。
该建复合索引还是拆UNION ALL,关键看OR的语义
如果 OR 是同一维度的多取值(比如 status = 'paid' OR status = 'refunded'),建单列索引或改用 IN 更自然;如果真要跨字段(user_id = 123 OR order_no = 'NO2026...'),复合索引毫无帮助,必须拆:
- 确保每个子句都能独立命中索引:
SELECT * FROM t WHERE user_id = 123和SELECT * FROM t WHERE order_no = 'NO2026...'各自有对应索引 - 显式写出列名,避免
SELECT *导致字段数/类型不一致报错ERROR 1222 - 原查询带
LIMIT或ORDER BY,必须在外层包裹:(SELECT ...) UNION ALL (SELECT ...) ORDER BY created_at DESC LIMIT 20
真正容易被忽略的点是:哪怕两个字段都有单列索引,OR 查询依然可能放弃所有索引——这不是配置遗漏,是优化器基于成本模型的主动放弃。别猜它怎么想,用 EXPLAIN 看 type 和 key 才算数。











