index merge 并不必然更快,其实际执行需多次索引扫描、主键归并及回表,i/o 与 cpu 开销远超联合索引;mysql 8.0.19+ 默认关闭该功能,优先建等值+范围顺序的联合索引才是根本解法。

EXPLAIN看到index_merge不等于真快
看到type: index_merge就以为“用了多个索引所以更快”,这是最常见的误判。MySQL 真正执行时,要分别扫描多个二级索引 → 提取各自满足条件的主键 → 在内存或磁盘做集合运算(交集/并集/排序)→ 最后统一回表取数据。整个链路比单次 B+ 树遍历的联合索引多出至少 2–3 轮随机 I/O 和 CPU 运算。
实操建议:
- 盯住
Extra字段:必须出现Using intersect()、Using union()或Using sort_union()才算真正启用;光看type不够 - 对比
rows:合并后的预估行数应明显小于任一单索引的rows,否则说明归并没起到过滤作用 - 检查
key_len:如果很小(比如只用到单列索引前缀),大概率是合并后仍需大量回表,性能反被拖累
WHERE a = ? AND b > ?走index_merge不如INDEX(a, b)
当查询含等值 + 范围条件(如 status = 'paid' AND created_at > '2024-01-01'),index_merge_intersection 要求两个条件都必须是完整等值匹配才能触发。而 created_at > ... 是范围查询,根本不符合 intersection 前提——此时若还看到 index_merge,八成是 sort_union,它得先各自扫索引、再排序归并,开销更大。
实操建议:
- 建联合索引优先按「等值字段在前、范围字段在后」排列,例如
INDEX(status, created_at) - 已有
INDEX(a)和INDEX(b),又频繁执行WHERE a = ? AND b > ?,直接删掉两个单列索引,换联合索引 - 别信“多个索引总比一个强”——B+ 树一次定位比多次归并稳定得多
OR条件触发index_merge_union但实际更慢
WHERE status = 'active' OR is_new_arrival = 1 这类查询容易走 index_merge_union,但只要任一条件选择性差(比如 is_new_arrival 只有 0/1 两值),它返回的主键集就可能占全表 50% 以上。归并后仍要回表大量数据,Extra 里常带 Using where,rows 居高不下。
实操建议:
- 对低基数字段(性别、状态、开关类)单独建索引意义极小,别为
OR惯性补单列索引 - 改写 SQL 更可靠:用
UNION ALL显式拆分,还能配合LIMIT提前终止 - 确认
optimizer_switch中index_merge_union是否真开启:SELECT @@optimizer_switch,避免本地测试失效
MySQL 8.0.19+ 默认关掉了index_merge
从 8.0.19 开始,MySQL 默认把 index_merge=off,不是 bug,是官方态度转变:它承认 Index Merge 是权衡下的妥协方案,不是优化目标。你看到生产环境没走 index_merge,未必是配置错,很可能是默认就被拦住了。
实操建议:
- 升级到 8.0.19+ 后,别急着开
index_merge,先用EXPLAIN FORMAT=TREE看优化器真实选了什么 - 若确实需要临时启用,用
SET SESSION optimizer_switch='index_merge=on',但仅限诊断,不建议长期打开 - 真正的解法永远是补联合索引,而不是调优开关——后者治标,前者治本
最易被忽略的一点:Index Merge 的归并过程无法利用 ORDER BY 或 LIMIT 提前剪枝。哪怕你只想要前 10 条,MySQL 也得先把所有符合条件的主键归并完,再回表、再排序、再截断。联合索引则可能在 B+ 树遍历时就命中 LIMIT,省下大半工作量。











