一次Hive数据倾斜优化

背景

公司最近在利用hive构建数仓,听同事们说在构建一个超宽的维度表时运行时长超6000s,这个时长肯定是不能接受的,不过倒是引起了我的兴趣,让同事把sql发过来看看。

现象

拿到sql初步分析,并没有发现很明显的大问题,就是10个千万级(3千万到6千万)的表做left join,部分表有一些group by,以及rank去重的一些操作。起初以为又是涉及的源表统计信息未更新导致执行计划走错了。首先就将sql涉及的表提取出来,进行统计信息更新,然后执行sql,看见进度一点点涨上去,还以为真就是统计信息的问题。600s左右进度已经达到99%,还剩下最后一个reducer。当时也还有其他事情,也就让他一直跑着,顺便看看要跑多久才能完,快到6000s的时候,还是没有完的迹象,放弃了,杀掉它吧。

分析

从上面的现象看来,很明显发生了数据倾斜,而且应该是很严重的倾斜。但是数据倾斜往往是发生在join key重复比较严重的时候,但是这条sql里面参与join的基本都是可以做主键的列,重复度很低。

我的第一个反应是,join的hash算法刚好针对这些key产生了很差的数据分割,导致绝大部分数据最后分到了一个reducer中去。首先尝试了将key计算md5后再做join,几乎没有效果。但还是不死心,也想搞清楚,hive执行join时,是根据什么算法将key分散到不同reducer。可是Google、百度都没有找到很确切的说法,如果真要搞清楚,估计只能去读源码了。但也说明,其他人在解决数据倾斜问题的时候,这个点应该是不会构成问题。

再继续分析sql吧,先看看这条sql的Tez DAG。

一次Hive数据倾斜优化_第1张图片
数据倾斜时的DAG

既然是在卡最后一个reducer,那么就把图中框起来的部分表单独拿出来跑,果不其然,这几个表单独跑也会发生数据倾斜的情况。进一步剔除一部分表,同时为了尽量缩短时间,只将这几个表需要关联的列拿出来操作,最后定位到如果与crcd和db进行join时就会导致不可接受的倾斜时长。这几个表中,rco,cm,dm,cd都是通过dm的id进行关联,crcd再通过crcd的id与cd的属性列关联,db再通过自己的id与crcd的属性列关联。只需要取出cd,crcd,db依次进行left join就会导致严重数据倾斜。当时又想去找出hive究竟按什么算法对key进行分割,不过又一想还不如看看倾斜后的数据究竟是什么样子。

怎么看呢?第一步执行下面类似的语句针对这三个表的join创建一个临时表(hive的默认情况下,每个reducer都会产生一个hdfs文件)。

create table tmp_cd_db as

select cd.crcd_id,db.id from cd left join crcd on cd.crcd_id = crcd.id left join db on crcd.db_id = db.id;

第二步,直接到hdfs上这个临时表的目录下,把最大的那个文件拷下来hadoop fs -copyToLocal ....

第三步,vi打开文件。

打开文件的一瞬间就明白为什么会倾斜了。

根本原因

打开文件的第一瞬间,除了crcd_id有值,db.id的值全部为NULL。再用hive sql验证一下,统计出db.id为NULL的行数约3000万,而非NULL的行数只有20多万。原来是大量重复的NULL导致了倾斜。20多万行用1000个reducer处理,3000万行一个reducer处理,当关联上220列数据时,这最后一个reducer的处理时长又会放大百倍左右。(注:这里并不是crcd与db关联不上产生的NULL,而是cd与crcd关联时关联不上,产生了大量的为空值的crcd.db_id,而再根据这些空值与db关联而导致的倾斜。)

另外一个方面的原因,还是数据质量存在问题,且建模前,也未对数据质量进行判断,就整合了这部分数据整合到模型。

解决办法

解决办法就很简单了,如果这能关联上的20万数据可以暂时不要的话,那么只需要简单去掉这几个表(关联crcd,ccd,dmb都是为了取dmb的属性)。只是简单去掉这几张表,其他不做任何调整,总时长在300s左右,当然还有一些小地方可以做一些优化。

如果需要这能关联上的20万条数据,那么就先让crcd与db关联后再与其它表关联生成最后的结果。

总结

1. outer join后如果还要继续进一步join,一定要注意关联不上的数量,避免出现大量NULL而导致数据倾斜。

2. 建模时一定要考虑数据质量。

你可能感兴趣的:(一次Hive数据倾斜优化)