mysql index merge 是优化器在单表查询中权衡成本后主动选择的访问路径,触发需满足三条件:仅单表、各or分支对应不同列且均有独立索引、合并成本低于单索引或全表扫描。

MySQL 的 Index Merge 是怎么触发的
它不是默认“开箱即用”的魔法,而是优化器在特定条件下权衡成本后主动选择的访问路径。触发前提有三个硬性条件:
• 查询只涉及单张表(JOIN 多表时不会对跨表索引做 merge)
• 每个 OR 分支都落在不同列上,且这些列各自有独立索引(例如 WHERE a = 1 OR b = 2,a 和 b 各有单列索引)
• 优化器估算出:分别走两个索引再合并结果,比全表扫描或只走一个索引 + 过滤更便宜
注意:WHERE a = 1 OR a = 2 这种同列多值,走的是单索引的等值查找(可能用到 range 类型),不触发 Index Merge。
EXPLAIN 中看到 index_merge 说明什么
这是关键信号,但别急着拍手——它只表示优化器“打算”用索引合并,并不保证高效。
• type 列显示 index_merge
• key 列会列出实际参与的索引名,如 idx_a,idx_b
• Extra 字段会出现 Using union(idx_a,idx_b) 或 Using intersect(...),明确告诉你合并策略
容易踩的坑:Using union 看似合理,但如果其中一个索引返回几百万行(比如低选择性字段),合并过程会消耗大量 CPU 和临时内存,甚至比全表扫描还慢。这时候 EXPLAIN 的 rows 值往往严重失真,不能轻信。
Index Merge Union 的实际执行流程
以 SELECT * FROM t WHERE c1 = 'x' OR c2 = 'y' 为例,InnoDB 的真实步骤是:
• 用 idx_c1 扫描所有 c1 = 'x' 的主键 ID,得到集合 A
• 用 idx_c2 扫描所有 c2 = 'y' 的主键 ID,得到集合 B
• 对 A 和 B 做去重并集(union),生成最终主键列表
• 根据这个主键列表,按顺序回查聚簇索引(也就是主键索引)取完整行数据
关键点:中间结果(A、B)只存主键,不取整行;但最后一步回表是随机 IO,如果主键分布太散,性能会断崖下跌。这也是为什么有些场景下手动改写成 UNION ALL 反而更快——你能控制是否去重、是否排序、甚至加 LIMIT 提前截断。
为什么生产环境常关闭 index_merge
这不是保守,而是被现实毒打后的选择。
• 统计信息不准时,优化器高估了索引选择性,选了 index_merge 却导致 10 倍慢查询
• index_merge 不支持覆盖索引(即无法避免回表),哪怕你只查 c1 和 c2 两列,也必须回聚簇索引拿整行
• MySQL 5.7 及之前版本的 sort_union 实现有内存泄漏风险,大结果集可能 OOM
京东、美团等公司线上都曾因开启该特性引发雪崩,所以运维侧普遍在 my.cnf 中设 optimizer_switch='index_merge=off'。如果你发现 OR 查询没走索引,先检查这个开关是否被关了,而不是怪 SQL 写得不对。











