UNION和UNION ALL的核心性能差异就在“去重排序”这一步。下面从执行过程、耗时占比、优化方向三个维度来分析。一、执行过程对比UNION ALL无去重text子查询1 → 结果集1 子查询2 → 结果集2 ↓ 直接拼接 ↓ 返回结果操作逐行追加O(n) 复杂度。内存几乎无额外开销。UNION去重text子查询1 → 结果集1 子查询2 → 结果集2 ↓ 合并为临时表 ↓ SORT UNIQUE排序去重 ↓ 返回结果操作需要排序SORT去重UNIQUEO(n log n) 复杂度。内存需要额外内存存储临时表和排序缓冲区。二、性能耗损具体分析1. 排序开销CPUUNION需要对合并后的结果集进行全量排序通常按所有列排序。数据量越大排序耗时越长O(n log n)的增长速度远高于UNION ALL的O(n)。2. 内存开销临时表UNION会在内存或磁盘上创建临时表来存储排序去重后的结果。如果结果集很大会溢写到磁盘Using temporary; Using filesort导致 I/O 剧增。3. 去重比较开销去重需要逐行比较所有字段字段越多、字段越长如VARCHAR(5000)比较成本越高。如果包含TEXT/BLOB字段UNION甚至无法直接去重需转换。4. 执行计划示例sqlEXPLAIN SELECT id, name FROM t1 UNION SELECT id, name FROM t2;输出中会出现Using temporary使用了临时表Using filesort使用了文件排序而UNION ALL则只有Append直接追加无temporary/filesort。三、性能数据对比参考数据量UNION ALL耗时UNION耗时性能差距1000 行~5ms~15ms3 倍10 万行~50ms~500ms10 倍100 万行~500ms~8s16 倍数据来源MySQL 5.7两表各 50 万行结果集 100 万行。实际差距因硬件、索引、字段类型而异。四、什么时候用UNION什么时候用UNION ALL场景推荐原因结果集必然不重复UNION ALL完全不需要去重业务允许重复UNION ALL性能更好必须保证唯一性但数据量小 1000UNION开销可接受必须保证唯一性数据量大考虑用UNION ALL 业务层去重避免数据库排序压力有索引列可以去重UNION ALL 应用层去重可以分批处理减轻 DB 压力五、优化建议1. 尽量用UNION ALL 业务层去重sql-- 数据库层只负责合并 SELECT id, name FROM t1 UNION ALL SELECT id, name FROM t2;然后在 Java 中用Set/Stream.distinct()去重把 CPU 压力转移到应用服务器。2. 用EXISTS或JOIN替代UNION有时候UNION可以用OR或IN替代sql-- 原查询使用 UNION SELECT id FROM t1 WHERE status 1 UNION SELECT id FROM t2 WHERE status 1; -- 优化后使用 OR走索引合并 SELECT id FROM t1 WHERE status 1 UNION ALL SELECT id FROM t2 WHERE status 1 AND NOT EXISTS (SELECT 1 FROM t1 WHERE t1.id t2.id);3. 加索引如果UNION的排序去重是基于索引列的MySQL 可以用索引顺序来避免filesortsql-- 对 id 建立索引UNION 去重时可以直接利用索引顺序 CREATE INDEX idx_id ON t1(id); CREATE INDEX idx_id ON t2(id);4. 减少SELECT *只选必要列列越少排序去重比较越快。5. 分页场景先UNION ALL再分页sqlSELECT * FROM ( SELECT id, name FROM t1 UNION ALL SELECT id, name FROM t2 ) t ORDER BY id DESC LIMIT 20;如果先UNION去重再排序分页性能会更差。六、总结维度UNION ALLUNION时间复杂度O(n)O(n log n)临时表不产生产生文件排序不产生产生适用场景默认首选仅在确实需要去重时使用最终建议能用UNION ALL就绝不用UNION。如果必须去重优先考虑在应用层做减轻数据库压力。如果数据量小 1 万行UNION的性能差异可以忽略。