背景
上个月,做了一个业务背景比较复杂,涉及表众多的etl处理,在整个过程中遇到了很多坑,其中最大的一个坑是,在整个s q l的处理过程中,有一段sql的执行花费时间较长,执行了二十多个小时未结束。严重影响了etl的进度,对此进行了一些列的优化,但效果甚微。
解决
具体的sql如下:(涉及业务隐私,表名和列名就简化了)
select * from
(select * from a1 where filedate='${filedate}') t1 left join
(select * from a2 where filedate='${filedate}') t2 left join
(select * from a3 where filedate='${filedate}') t3 left join
(select * from a4 where filedate='${filedate}') t4 left join
(select * from a5 where filedate='${filedate}') t5
on t1.zd1=t2.zd1 and t1.zd2=t2.zd2 and t1.zd3=t2.zd3 and
t1.zd4=t3.zd4 and t1.zd5=t4.zd5 and t1.zd6=t5.zd6;
针对上述sql的优化,显示做了列剪切,避免select *的出现,可是几乎没有看到效果,经过讨论,决定增加reducer的数量,这一来就发现了问题,在执行过程中,reducer的数量始终为1,由于关联复杂,表数据量又大,一个reducer执行以上sql明显需要更多时间,于是对上述sql进行了一番改动,为了方便写,我这里依然写成了select *,建议开发过程中,如果字段很多的表,尽量做一些字段的切割,用到哪些字段就select哪些。
改造后的sql如下:
select * from (
select * from (
select * from (
select * from (
(select * from a1 where filedate='${filedate}') t1 left join
(select * from a2 where filedate='${filedate}') t2
on t1.zd1=t2.zd1 and t1.zd2=t2.zd2 and t1.zd3=t2.zd3
)h1 left join
(select * from a3 where filedate='${filedate}') t3
on h1.zd4=t3.zd4
)h2 left join
(select * from a4 where filedate='${filedate}') t4
on h4.zd4=t4.zd4
)h3 left join
(select * from a5 where filedate='${filedate}') t5
on h3.zd4=t5.zd4
)h4
结果与分析:
这次改造之后,运行时间大概需要5分钟,并且可以观察到,reducer数量达到了500+,改造的思路就是将两个表的left join之后的结果作为一个结果集与下一张表进行left join。
hive优化相关总结
在使用hive做etl处理时,数据量大并不是主要问题,问题是数据倾斜。避免数据倾斜,可以有效提高s q l的执行效率。
对列进行裁剪
hive在读数据时,可以只读查询中所需要的列,忽略其他的列。对分区进行裁剪
数据量大的时候,尽可能通过分区进行查询或过滤减少count、distinct、order by的使用
合理减少job的数量
job较多的任务,运行的效率会相对较低,如果经过多次关联,产生十几个甚至更多的job,会至少需要三十分钟的时间,用来对任务的分配,初始化数据和数据输出。而初始化任务的这个过程比较好费时间。在join的时候尽可能的将小表放在左边
因为join左边的表在reduce阶段会被放到内存中主键为null会产生数据倾斜
主键为null的值,会被作为相同的key被分配到同一个map计算中,可能会产生数据倾斜数据量大的去重操作使用group by代替distinct
增加map reduce task数量
适当的增加reducer的数量可以提高执行效率,但是不能过多,因为reducer数量过多,会产生过多的小文件,占用过多的内存。