跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

mysql如何优化OR条件的索引使用_mysql索引合并(Index Merge)

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'
改写为: MySQL(Linux) MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。 下载
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
手动控制执行路径。

相关文章