UNION ALL与UNION的性能差异及选择技巧

73 阅读5分钟

一、底层机制解析

UNIONUNION ALL作为SQL集合操作的核心运算符,其性能差异源于数据处理的本质区别:

  1. 排序去重机制
    当使用UNION时,数据库引擎会自动执行以下流程:
(SELECT * FROM table1)
UNION
(SELECT * FROM table2)
-- 等价于
SELECT DISTINCT * FROM (
  SELECT * FROM table1 
  UNION ALL 
  SELECT * FROM table2
) AS combined_data

系统需要创建临时表存储中间结果集,执行排序操作消除重复记录。以MySQL为例,通过EXPLAIN可观察到Using temporaryUsing filesort标记。

  1. 数据扫描成本
    UNION ALL采用流式处理机制,仅需顺序扫描各子查询结果集。而UNION的排序去重操作会引发以下额外消耗:
    • 内存/磁盘临时空间占用
    • 索引失效风险(排序破坏原有索引顺序)
    • CPU计算资源消耗(比较重复值)

二、性能对比实测

通过TPC-H测试数据集进行基准测试(单位:毫秒):

数据量UNION ALLUNION差异倍数
10万行826207.56x
50万行3953,8509.75x
100万行8109,20011.36x

测试环境:MySQL 8.0,InnoDB引擎,SSD存储

随着数据量增长,UNION的时间复杂度呈非线性上升趋势,主要瓶颈出现在:

  • 临时表写入的I/O延迟
  • 排序阶段的比较操作(O(n log n)复杂度)
  • 内存不足时触发的磁盘交换

三、典型应用场景抉择

优先使用UNION ALL的情况

  1. 日志数据合并
    处理访问日志、设备状态记录等天然具有时序特征的数据时:
SELECT * FROM access_log_2023
UNION ALL
SELECT * FROM access_log_2024
  1. 维度表跨库查询
    整合多个分库的用户信息表时,即便存在理论上的重复可能,实际业务中ID具有唯一性:
SELECT user_id, name FROM user_db1.users
UNION ALL 
SELECT user_id, name FROM user_db2.users
  1. 中间结果集处理
    在CTE(通用表表达式)中构建中间数据集供后续筛选:
WITH combined AS (
  SELECT product_id FROM inventory 
  UNION ALL
  SELECT item_id FROM archived_items
)
SELECT * FROM combined WHERE ... 

必须使用UNION的场景

  1. 数据清洗阶段
    合并多个来源的客户数据时消除重复记录:
SELECT customer_id FROM crm_system
UNION
SELECT client_id FROM legacy_db
  1. 统计维度归并
    当多个查询结果可能存在语义重复时(如不同统计口径的数据汇总):
SELECT 'monthly', SUM(amount) FROM orders
UNION
SELECT 'quarterly', SUM(amount) FROM orders GROUP BY QUARTER(order_date)

四、高阶优化策略(接续前文)

1. 索引加速技巧

覆盖索引设计
UNION必须使用时,为子查询字段创建覆盖索引可减少排序阶段的磁盘I/O。以电商订单合并场景为例:

-- 创建复合索引避免回表
CREATE INDEX idx_order_union ON orders(region, order_date, amount)

-- 优化后的UNION查询
SELECT region, order_date, amount FROM orders_asia
UNION
SELECT region, order_date, amount FROM orders_europe

哈希去重替代
在PostgreSQL等支持哈希聚合的数据库中,通过调整配置参数启用哈希去重:

SET enable_sort = off;
SELECT * FROM table1
UNION
SELECT * FROM table2  -- 此时使用HashAggregate而非Sort

2. 执行计划干预

强制索引使用
当优化器错误选择全表扫描时,使用FORCE INDEX提示:

(SELECT * FROM log_2023 FORCE INDEX(timestamp_idx))
UNION
(SELECT * FROM log_2024 FORCE INDEX(timestamp_idx))

临时表优化
通过调整tmp_table_size参数控制内存临时表使用:

# MySQL配置
tmp_table_size = 64M
max_heap_table_size = 64M

3. 分布式架构适配

分片数据预处理
在TiDB等分布式数据库中,先在分片节点去重再汇总:

-- 各分片执行
SELECT DISTINCT * FROM local_table

-- 协调节点执行
SELECT * FROM shard1_result
UNION ALL
SELECT * FROM shard2_result

并行执行控制
Oracle开启并行查询加速大数据量合并:

SELECT /*+ PARALLEL(8) */ col1 FROM tab1
UNION ALL
SELECT /*+ PARALLEL(8) */ col1 FROM tab2

五、特殊场景解决方案

1. 海量数据分页陷阱

直接对UNION结果分页会触发全量计算,采用子查询分层处理:

WITH temp AS (
  SELECT id, name, 1 AS source FROM big_table1
  UNION ALL
  SELECT id, name, 2 AS source FROM big_table2 
)
SELECT * FROM temp 
ORDER BY id LIMIT 100 OFFSET 10000

2. JSON数据合并处理

合并JSON字段时需注意格式一致性:

SELECT JSON_EXTRACT(payload, '$.event') AS event
FROM log_2023
UNION ALL
SELECT CAST(data->>'action' AS JSON)  -- 显式类型转换
FROM log_2024

3. 跨异构数据库同步

通过中间件转换字段类型后合并:

-- 从MongoDB和MySQL同步数据
SELECT 
  _id AS doc_id,
  title,
  CAST(created_at AS DATETIME)  
FROM mongo_articles
UNION ALL
SELECT
  id,
  title,
  publish_time
FROM mysql_articles

六、最佳实践清单

  1. 预判数据特征

    • 源表主键重叠率低于5%时优先UNION ALL
    • 高重复概率场景(>30%)可接受UNION成本
  2. 执行计划验证

    • 关注Extra列中的Using temporary警告
    • 对比实际执行时间与估算值的偏差率
  3. 资源监控指标

    # MySQL内存监控
    SHOW STATUS LIKE 'Created_tmp%';
    
    # PostgreSQL排序统计
    SELECT * FROM pg_stat_database WHERE datname = 'your_db';
    
  4. 降级兜底方案
    UNION超时时可分阶段执行:

    -- 第一阶段:存入临时表
    CREATE TEMPORARY TABLE tmp_data AS
    SELECT * FROM table1 UNION ALL ...;
    
    -- 第二阶段:去重处理  
    SELECT DISTINCT * FROM tmp_data;
    

深度思考:在云原生架构下,传统UNION操作面临计算下推与数据分片的新挑战。建议结合物化视图实现预聚合,或采用UNION ALL +窗口函数进行二次去重,例如:

SELECT * FROM (
  SELECT *, ROW_NUMBER() OVER(PARTITION BY key_col) AS rn
  FROM (
    SELECT * FROM shard1
    UNION ALL
    SELECT * FROM shard2
  ) merged
) ranked
WHERE rn = 1

此模式可将去重压力分散到查询层,避免集中式排序带来的系统性风险。




🌟 让技术经验流动起来

▌▍▎▏ 你的每个互动都在为技术社区蓄能 ▏▎▍▌
点赞 → 让优质经验被更多人看见
📥 收藏 → 构建你的专属知识库
🔄 转发 → 与技术伙伴共享避坑指南

点赞 ➕ 收藏 ➕ 转发,助力更多小伙伴一起成长!💪

💌 深度连接
点击 「头像」→「+关注」
每周解锁:
🔥 一线架构实录 | 💡 故障排查手册 | 🚀 效能提升秘籍