union和union all的性能差别居然这么大!!
最近做个项目,要把我们系统中的用户数据,按照别人系统的格式要求生成数据文本,导入到别的系统中。
我们系统的用户数据主要存放在两个表中,用户信息表(user)和用户附加信息表(extend)。user表中主要是用户id,用户昵称、用户名等信息,extend表中主要是手机号、身份证、核心客户号码等信息。每个表的主键都是用户id(字段名为uid),分别有1.6亿数据,关联查询导出即可。
查询要求
核心客户号(cusnum)有效的用户,即不为空、不为零。
核心客户号(cusnum)为空,身份三要素(姓名name、证件类型certType、证件号码certNum)有效的情况。
SQL语句
查询SQL非常简单,分别将满足条件的数据查出来做个合并,导出至指定文件即可。
// 核心客户号有效
select ...
from user u inner join extend e
on u.uid=e.uid
where e.cusnum IS NOT NULL and e.cusnum != '' and e.cusnum != 0
union
// 核心客户号为空,身份三要素有效
select ...
from user u inner join extend e
on u.uid=e.uid
where (e.cusnum IS NULL or e.cusnum = '' or e.cusnum = 0)
and (e.name IS NOT NULL and e.name != '')
and (e.certType IS NOT NULL and e.certType != '')
and (e.certNum IS NOT NULL and e.certNum != '')
into outfile '/xxxxx/xxxx.txt'
但是使用这个SQL,导出全部1.6亿的数据竟然用了14个小时!!!
这个时间已经远远超过了投产的时间窗口,1.6亿数据虽然会慢,但没道理会这么慢呀。
把两个SQL分别执行导出,每个语句执行了不到20分钟就完成了,但是使用union在一起竟然会变得这么慢。
union机制
随后,我就查了查mysql的union机制,才知道union不仅对多个sql的查询结果做了合并,还在合并的基础上做了默认排序,同时还去除了重复行。1.6亿的数据进行排序、归并,想想就头疼。
如果不对查询结果进行排序、去重的话,可以使用union all。
union all 只是简单的将两个结果合并后就返回。如果返回的两个结果集中有重复的数据,那么返回的结果集就会包含重复的数据了。
在我们的这个场景中,第一个语句的条件是cusnum有效,第二个语句的查询条件是cusnum无效,因此不会有重复的数据。而且我们只要把数据全部导出即可,不关注数据的顺序,因此我们可以用union all来替换union。
修改后再次执行,这次只花费不到30分钟就把数据导出了,效率提升了28倍。如果数据量更大的话,这个效率提升会更明显。
总结
UNION ALL 要比UNION快很多,所以,如果可以确认合并的两个结果集中不包含重复数据且不需要排序时的话,那么就使用UNION ALL。