产生数据倾斜的原因
首先给出个结论:产生数据倾斜的最根本的原因是key数量在reduce阶段的分布特别不均匀,某些reduce需要处理非常多的一些key,该部分的reduce会需要更长的时间来完成任务,而其他的reduce只需要处理相对少的其他key,对应也只需要相对较少的时间来完成任务。
为什么会出现key分布不均匀这样的情况呢?
这是由于hive底层使用的是MapReduce计算框架,如下图所示,Mapper Task会将文件读取到内存并进行相应处理后以<key, value>形式写回到磁盘,在处理的阶段会对key进行分区和排序。而Reducer Task则会读取经过Mapper Task任务处理后生成的文件中属于本Reducer Task的分区。因此,如果在Reducer Task阶段某些Reducer Task读取了拥有数量很多的key的分区,就会造成key数量分布的不均匀。
解决办法
现在我们已经知道了是由key数量分布不均匀导致的数据倾斜,也知道了是什么原因导致的key数量分布不均匀,下面介绍一下处理数据倾斜问题的解决办法。根据大量的key是否为null我们分别可以采用不同的处理办法。
- 大量的key为null值
- 可以为null值的key赋于随机值,从而使得该部分key去往不同的Reducer Task。
- 大量的key不为null值
- 由group by导致的数据倾斜
- 可以通过设置hive.map.aggr=true参数和hive.groupby.skewindatda=true来解决。前者参数表示在Mapper Task进行预聚合,使得发送到Reducer Task的key数量大幅减少。
后者参数表示会启动两个job来完成任务,前一个job的Mapper Task产生的文件并不是根据分区发送到指定的Reducer Task, 而是随机发送给Reducer Tasks们,也就是相当于在该reduce阶段进行了预聚合,之后再启动一个job进行正常的MapReduce。
- 由join导致的数据倾斜
- 如果是大小表join,则可以采用map join的方式解决。原理就是在Mapper task阶段将小表先加载进内存,然后从磁盘读取大表进行join操作。此方式没有Reducer Task,因此不会也就不会有数据倾斜的问题
- 如果是大表与大表join,则可以设置skew join, 并设置判断发生数据倾斜的key的数量。其原理是:对不倾斜的部分正常处理,对判断发生倾斜的key先不在Reducer端处理,而是先写入hdfs,然后在启动新一轮的map join单独处理该key。如下图所示。