MySQL优化器如何处理UNION与UNION ALL 在数据库优化工作中,UNION和UNION ALL这对看似简单的集合操作符,背后隐藏的执行逻辑差异却常常被低估。很多性能问题,追根溯源,就出在对它们内部机制的理解偏差上。 UNION 和 UNION ALL 的执行计划差异 先明确一个核心区别:

在数据库优化工作中,UNION和UNION ALL这对看似简单的集合操作符,背后隐藏的执行逻辑差异却常常被低估。很多性能问题,追根溯源,就出在对它们内部机制的理解偏差上。
长期稳定更新的攒劲资源: >>>点此立即查看<<<
先明确一个核心区别:MySQL优化器处理UNION和UNION ALL的路径截然不同。关键在于UNION默认要去重,而UNION ALL则完全跳过这一步。这意味着,哪怕你写的是UNION(它本质就是UNION DISTINCT的简写),只要没显式声明ALL,优化器就必须启动一套包含临时表和排序或哈希的去重流程。
ORDER BY或LIMIT,UNION还可能触发额外的物化步骤——比如先让每个子查询各自排序,然后再进行合并,这无疑增加了复杂度。这里有个常见的误区:直接用EXPLAIN查看UNION语句时,去重这个关键操作往往不会单独显示为一行的执行计划。它被“隐藏”在了Extra字段里,常见的提示包括Using temporary、Using filesort。有时甚至只显示Using union(...),对去重只字不提,很容易让人误判执行成本。
EXPLAIN FORMAT=JSON才能揭示完整流程。你会看到类似"union_result": {"using_temporary_table": true}的明确信息。UNION可能导致两层临时表的创建——子查询一层,合并去重又一层。UNION ALL,它的EXPLAIN输出通常很“干净”,只展示各子查询的独立计划,Extra字段里一般找不到Using temporary的踪影。MySQL对集合运算后的排序和分页有严格规定:必须写在最外层。这不是简单的语法洁癖,而是因为优化器无法智能地将分散在各个子查询中的ORDER BY或LIMIT提升到合并后的结果集上生效。
(SELECT id FROM t1 ORDER BY id LIMIT 10) UNION (SELECT id FROM t2 ORDER BY id LIMIT 10)。这样写,数据库并不会保证最终结果是全局有序的。(SELECT id FROM t1) UNION ALL (SELECT id FROM t2) ORDER BY id LIMIT 10,让排序和限制作用于最终合并集。(SELECT * FROM (SELECT id FROM t1 ORDER BY id LIMIT 10) t1s) UNION ALL ...。但需要警惕,这通常会强制结果集物化,可能带来更大的性能损耗。这个问题容易被忽略。MySQL在判断两行是否重复时,对于NULL值采用的是三值逻辑(即NULL = NULL的结果是UNKNOWN,但在去重时却视它们为相等)。这种特殊的处理方式意味着,结果集中包含的NULL字段越多,基于哈希的去重算法失败的概率就越高,优化器很可能因此回退到基于排序的去重方式,导致I/O和CPU开销双双上升。
NULL时,UNION使用的临时表很容易从内存(受tmp_table_size限制)溢出到磁盘,速度急剧下降。UNION ALL则完全不受此问题困扰,因为它根本不做任何行间比较。UNION。尤其是在大表关联或子查询返回列数较多的情况下,改用UNION ALL带来的性能提升可能是数倍级的。说到底,真正卡住性能脖子的,往往不是UNION这个语法本身,而是其背后触发的去重逻辑在何时、以何种方式执行。当一个本身已经通过索引覆盖而高效的子查询,仅仅因为套了一层UNION就被迫走入临时表的慢车道时,这种性能损耗最容易被忽视,也最值得深入排查。
侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述