union all本身开销极低,性能骤降主因是子查询未优化:某子查询全表扫描、返回冗余字段或未下推过滤条件,导致临时表溢出磁盘;须逐个检查explain、建覆盖索引、显式cast对齐类型、在子查询内加limit并避免外层复杂排序。

为什么UNION ALL会突然变慢
UNION ALL本身不排序、不去重,开销极低;性能瓶颈几乎总是出在子查询上。常见现象是:单个SELECT跑得很快,但套进UNION ALL后整体耗时翻几倍,甚至触发ERROR 2013 (HY000): Lost connection to MySQL server during query。根本原因是MySQL先执行所有子查询、把结果全写入临时表,再统一返回——如果某个子查询没走索引、或返回了大量冗余字段,就会卡在I/O或内存溢出上。
每个子查询必须独立优化
别指望优化器自动帮你下推条件或复用索引。你得手动确保每个SELECT都高效:
- 对每个子查询的
WHERE、JOIN、ORDER BY涉及的列,单独建索引(比如table1(status, created_at)和table2(type, updated_at)不能共用一个索引) - 删掉
SELECT *,只写明业务真正需要的字段,尤其避免TEXT、BLOB列参与合并 - 把过滤条件明确写进每个子查询内部,而不是放在
UNION ALL外层包裹一层SELECT ... FROM (subquery1 UNION ALL subquery2) t WHERE ... - 检查
EXPLAIN输出里每个子查询是否都用了type=ref或range,而不是ALL(全表扫描)
临时表撑爆内存怎么办
UNION ALL结果集默认存入临时表,若超过tmp_table_size或max_heap_table_size,就会落盘到磁盘临时表,I/O暴增。典型症状是SHOW PROCESSLIST里看到Copying to tmp table on disk。
临时解决方法(需DBA权限):
- 调大两个参数:
SET SESSION tmp_table_size = 268435456;(256MB),SET SESSION max_heap_table_size = 268435456; - 但更治本的是控制结果集大小:在每个子查询末尾加
LIMIT(如业务允许),或在外层加LIMIT并确保ORDER BY字段有索引(否则仍要全量排序) - 避免在UNION ALL后做
GROUP BY或复杂ORDER BY——这些操作无法下推,必须等全部数据合并完才执行
字段类型和数量不一致的隐性开销
MySQL要求UNION ALL各子查询的字段数、顺序、类型兼容。一旦出现隐式转换(比如VARCHAR(50)和VARCHAR(100)合并),就会触发额外的类型转换和内存拷贝;若某列在部分子查询中为NULL、部分为INT,也会强制转成最宽类型。
检查和修复方式:
- 用
SELECT ... UNION ALL SELECT ...后接DESCRIBE,看列类型是否统一 - 显式用
CAST(... AS ...)对齐类型,例如统一写成CAST(name AS CHAR(100)) - 字段数必须严格一致——少一列就报错,多一列会截断,别依赖“看起来一样”
真正卡住的地方往往不是UNION ALL语法本身,而是子查询里那个被忽略的created_at > '2020-01-01'没走索引,或是临时表配置刚好卡在256MB临界点。优化得拆开看,不能当成一个整体去调。











