bigywater.tech
MySQL / InnoDB · 34

Index Merge

建一张有两个独立单列索引的表,验证OR条件触发Index Merge Union、AND条件触发Intersection,并测出比全表扫描快30倍。

Union(OR)
的Deduplicate合并策略
Intersection(AND)
的Intersect合并策略
约30倍
Index Merge比全表扫描快的倍数

做的事:一张表,两个独立的单列索引

a和b各自有自己的索引,互不相关,看OR/AND条件下优化器怎么用它们。

建表插入CREATE TABLE t16 (id INT PRIMARY KEY AUTO_INCREMENT, a INT, b INT, INDEX idx_a(a), INDEX idx_b(b)); 插入30万行,a、b各自随机1~1000

验证:OR条件,触发了两个索引的并集合并

a=500 OR b=500,理论上没有一个联合索引能直接满足。

结果EXPLAIN SELECT * FROM t16 WHERE a=500 OR b=500 -> Filter: ((t16.a = 500) or (t16.b = 500)) -> Deduplicate rows sorted by row ID -> Index range scan on t16 using idx_a over (a = 500) (rows=295) -> Index range scan on t16 using idx_b over (b = 500) (rows=328)

的执行方式是:分别用idx_a和idx_b各自做一次独立的索引范围扫描,拿到两组的行ID集合,再Deduplicate rows sorted by row ID——按行ID排序去重合并,避免同时满足a=500和b=500的行被重复统计两次。这就是Index Merge的Union(并集)策略。

验证:AND条件,触发了两个索引的交集合并

a=500 AND b=500,同样没有联合索引能一次搞定。

结果EXPLAIN SELECT * FROM t16 WHERE a=500 AND b=500 -> Filter: ((t16.b = 500) and (t16.a = 500)) -> Intersect rows sorted by row ID -> Index range scan on t16 using idx_a over (a = 500) (rows=295) -> Index range scan on t16 using idx_b over (b = 500) (rows=328)

这次是 Intersect rows sorted by row ID——两个索引各自找出满足单一条件的行ID集合,取交集,只留下同时满足两个条件的行。cost从Union的296降到了Intersect的0.77,因为交集通常比并集小得多。

验证:Index Merge比强制全表扫描快约30倍

用IGNORE INDEX强制对照。

结果Index Merge(OR条件,默认走法): actual time=0.0425..1.5 IGNORE INDEX强制全表扫描(同样OR条件): actual time=0.736..46
测出Index Merge比强制全表扫描快了约30倍。Index Merge解决的是一个很实际的问题:当WHERE条件涉及多个独立的单列索引组合(不是一个联合索引能覆盖的场景)时,优化器不必只能"选一个索引凑合用+对其余条件做全表过滤",也不必"直接放弃索引全表扫描",而是可以把多个单列索引各自的扫描结果,在行ID这个层面做合并(并集或交集),拼出一个比单独用任何一个索引、或者不用索引更优的执行路径。

分步演示:OR和AND触发的两种Index Merge

每一步都是本文上面用EXPLAIN和EXPLAIN ANALYZE跑出来的原始结果。

Index Merge用行ID合并把两个单列索引拼成一条更优路径
这一步新验证的条件

    本文用到的工具

    • 本机 MySQL 26.7.0(InnoDB),30万行、两个独立单列索引的表
    • EXPLAIN / IGNORE INDEX:对比Index Merge和全表扫描
    京ICP备2026067502号-1京公网安备11010502063487号