登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  数据库 >  MySQL

MySQL OR 条件什么时候会选择 Index Merge

来源:17golang原创

时间:2026-09-10 14:09:44 373浏览 收藏

MySQL 的 OR 条件并不是“有两个索引就一定分别查一遍”。当同一张表的多个条件能够转换成独立的范围扫描,而且合并这些扫描的成本低于其他访问路径时,优化器才可能选择 Index Merge。最可靠的判断方法是看 EXPLAINtype 出现 index_mergekey 列列出多个索引,Extra 再显示 Using union(...)Using sort_union(...)

官方地址:https://dev.mysql.com/doc/refman/8.4/en/index-merge-optimization.html

要点速览
  • 等值 OR 常见于 Index Merge union,范围 OR 可能落到 sort_union。
  • 出现 index_merge 只说明优化器选了这种计划,不代表它一定比复合索引快。
  • 先看 rows、回表列和 Extra,再用改写或单语句开关做对照。

先用 EXPLAIN 判断是否真的走 Index Merge

先准备一条边界清楚的查询。假设 orders 上分别有 customer_idstatus 索引,查询只关心两个入口之一命中的订单:

-- 用 EXPLAIN 观察 OR 条件实际选择了哪些访问路径
EXPLAIN SELECT order_id, customer_id, status, created_at
FROM orders
WHERE customer_id = 1088 OR status = '待支付';

重点记录四列,而不是只盯着一行“命中索引”的结论:

字段怎么看
typeindex_merge 表示多个索引范围结果被合并。
key查看实际参与的索引名称,通常不止一个。
rows估算需要检查的行数;多个分支都很宽时,合并未必划算。
Extra区分 Using unionUsing sort_union 以及后续回表。

OR 条件为什么可能选择 Index Merge

Index Merge 只针对同一张表的多个 range 扫描。对于上面的两个等值分支,MySQL 可以分别从两个 B-Tree 索引拿到行标识,再做并集和去重;这通常对应 Using union(customer_id_idx,status_idx)。它不是跨表把两个查询拼起来,也不是把两个索引物理合成一个新索引。

MySQL orders 表中 customer_id 与 status 两个索引分支汇合为 Index Merge union 并回表的静态结构图
图1:同一张表的两个索引分支汇合为 Index Merge union 候选。

如果 OR 两边是较宽的范围,例如 created_at ...,普通 union 不一定适用,优化器可能先收集全部行标识、排序后再合并,Extra 会出现 Using sort_union(...)。而多个条件用 AND 组合时,才可能看到 intersect;不要把 intersect 的规则套到 OR 查询上。

发现计划不合适时怎么调整

Index Merge 的选择来自成本估算。两个单列索引虽然都能过滤,但最终 SELECT 还要取很多非索引列,就会产生大量回表;若两个条件的选择性都不高,先各自扫描再合并可能还不如一次顺序访问。可以用一条只取索引列的对照查询,确认回表是否是主要代价:

-- 只取两个索引列,帮助区分“合并索引”与“回表取整行”的影响
EXPLAIN SELECT customer_id, status
FROM orders
WHERE customer_id = 1088 OR status = '待支付';

如果业务查询总是围绕同一组条件,优先评估更贴合访问模式的复合索引;如果 OR 结构很深,可先按等价逻辑重新加括号,让每个分支更容易转换成范围条件。改动后必须重新看 keyrows 和实际耗时,不能只凭执行计划名称下结论。

用单语句对照实验确认取舍

需要判断 Index Merge 是否真的有益时,先在测试会话或低风险环境做对照。MySQL 通过 optimizer_switch 控制相关算法,也支持按语句影响优化器;一次只改变一个因素,避免把统计信息、索引和开关同时改掉。

-- 仅在当前会话关闭 Index Merge,和默认计划做同一条 SQL 的对照
SET SESSION optimizer_switch = 'index_merge=off';
EXPLAIN SELECT order_id, customer_id, status, created_at
FROM orders
WHERE customer_id = 1088 OR status = '待支付';

-- 对照完成后恢复默认开关,避免影响后续查询
SET SESSION optimizer_switch = 'index_merge=on';

对比时至少保留两份 EXPLAIN:默认计划和关闭合并后的计划。若关闭后变成单索引扫描但 rows 更大,不代表默认合并就绝对正确,还要结合返回行数、缓存命中、排序和线上延迟观察。生产环境更适合通过影子流量或灰度查询验证,再决定是否调整索引。

MySQL OR 查询从成本估算到 type=index_merge、key、key_len、rows 和 Extra 对照的静态技术图
图2:用 EXPLAIN 字段和成本判断对照 Index Merge 与复合索引候选。

相关问题

Index Merge 和复合索引哪个更快?

没有固定答案。条件稳定且组合规律明确时,复合索引可能减少合并与回表;条件变化大时,Index Merge 可能更灵活,最终以同数据分布下的计划和实测为准。

为什么写了 OR 却看不到 index_merge?

OR 两侧可能无法形成合适的 range,或者优化器估算其他路径成本更低。先检查括号、列上是否有可用索引,再看 rows 和统计信息。

能不能强制每个 OR 分支都使用索引?

不建议先强制。索引提示或优化器开关只适合有对照证据的场景,数据分布变化后原计划可能反而变慢。

判断 MySQL OR 条件是否会选择 Index Merge,核心不是背一个固定语法,而是把“条件形状—索引分支—合并方式—回表成本”连起来,再用 EXPLAIN 和可回滚的对照实验确认。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>