mysql对union各子查询索引使用完全隔离,每个select独立生成执行计划,无法跨子句复用联合索引,因union是集合操作而非连接操作,不构建行间关联,故优化器不会合并条件或协同选择索引。

MySQL 对 UNION 各子查询的索引使用是完全隔离的——它不会跨子句复用或共享索引,每个 SELECT 都被当作独立查询优化。这不是设计缺陷,而是由 UNION 的语义和执行模型决定的。
UNION 每个 SELECT 都走独立的查询计划
MySQL 在解析 UNION 时,会把每个 SELECT 子句拆开,分别调用优化器生成执行计划。这意味着:
- 第一个
SELECT的WHERE条件匹配idx_a,第二个SELECT即使查同一张表、同样字段,也得重新评估是否用idx_b或全表扫描 - 优化器不会“记住”前一个子句用了什么索引,也不会尝试把两个子句的条件合并后重选联合索引
-
EXPLAIN输出里你会看到多段独立的执行计划,每段的key和type可能完全不同
为什么不能“共用”一个联合索引?
因为 UNION 是集合操作,不是连接操作。它不构造行之间的关联关系,只拼结果集。所以:
- 没有
ON条件或关联字段可利用,无法触发跨子句的索引协同(比如JOIN中的驱动表+被驱动表索引配合) - 即使两个子句都查
user表且条件都是status = 'active' AND city = 'Beijing',只要没写成同一个SELECT,MySQL 就不会考虑为它们共同设计一个(status, city)联合索引并复用——它只对单个SELECT做索引选择 - 如果真想复用索引逻辑,得把条件合并到一个
SELECT里,用OR或IN,这时才可能触发index_merge_union或联合索引
常见误判:以为 UNION 能“自动合并索引条件”
很多人写完 SELECT * FROM t WHERE a=1 UNION SELECT * FROM t WHERE b=2,期待 MySQL 把两个条件“合起来”走一个 (a,b) 索引——但实际不会发生。原因包括:
-
UNION不改写WHERE逻辑,它只是结果拼接;优化器不会把a=1和b=2当作同一层过滤条件去重估 - 若表上只有
idx_a和idx_b,MySQL 可能分别走这两个索引(各扫一遍),再在内存中合并结果——这其实是index_merge_union的典型场景,但它发生在单个SELECT内部,而非UNION的两个子句之间 - 错误示范:
SELECT id FROM t WHERE a=1 UNION SELECT id FROM t WHERE b=2→ 这里两个子句各自独立,哪怕a和b有联合索引也无用;正确做法是改成单句:SELECT id FROM t WHERE a=1 OR b=2
真正容易被忽略的点在于:UNION 的性能瓶颈往往不在索引本身,而在结果集合并阶段(尤其是 UNION 默认去重需排序)。索引是否“独立”,本质是 MySQL 执行模型的刚性限制,不是调优能绕过的——想让多个条件共享索引,就得让它们落在同一个 SELECT 里。











