持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情
1. 数据倾斜
对于数据倾斜,我们知道这是数据中的一种常见情况。在数据运行中会不可避免地会出现离群值(outlier),这种情况就会并导致数据倾斜。
数据倾斜会显著地拖慢 MapReduce 的执行,具体的说会导致 MR实例进程中的 map 和 reduce 的任务执行时间大为延长,也会让需要缓存数据集的操作消耗更多的内存资源。
那么在MR框架中,什么因素会导致数据倾斜情况的出现呢?数据倾斜又具体分为几种情况呢?这里我们就来给你仔细解答一下。
(1)MapReduce数据倾斜出现原因
在MR框架中在 map 端和 reduce 端都有可能发生数据倾斜。
-
在 map 端的数据倾斜与切片机制有关,会让多样化的数据集的处理效率更低
-
在 reduce 端的数据倾斜常常来源于 MapReduce 的默认分区器(Partitioner)。
(2)数据倾斜的常见分类
-
数据频率倾斜——某一个区域的数据量要远远大于其他区域;
-
数据大小倾斜——部分记录的大小远远大于平均值。
下面我们简单介绍下 map端的切片 和 reduce端默认分区器
2. Map阶段 --- 切片
在正式介绍切片之前,我还是先说一下数据块以及数据切片的知识。
(1)数据块
数据块Block是HDFS物理上把数据分成一块一块,数据块是HDFS存储数据的单位。
(2)数据切片
数据切片只是在逻辑上对输入进行分片,并不会在磁盘上将其切分成片进行存储。数据切片是MapReduce程序计算输入数据的单位,一个切片会对应启动一个MapTask。特别要注意的是,切片时不考虑数据集整体,而是逐个针对每一个文件单独切片,如下图所示。
(3)数据切片公式
知道了数据块和数据切片,那我们就可以看一下MR源码中关于计算数据切片大小的公式。
Math.max(minSize, Math.min(maxS ize, blockSize)),
mapreduce.input.fileinputformat. sp1it.minsize=1 #默认值为1
mapreduce.input.fileinputformat. sp1lit.maxsize= Long.MAXValue #默认值Long.MAXValue
通过以上源码,我们可以知道,在默认情况下,数据切片的大小是等于块大小(blocksize)的。 因为我们之前说过切片时不考虑数据集整体,而是逐个针对每一个文件单独切片,所以说如果我输入两个文件,其中一个文件大小是 Blocksize 的6倍,而另一个文件的大小只有 Blocksize 的1/2,那么在这种情况下就有极大的概率导致数据频率倾斜,也就是某一个区域的数据量要远远的大于其他区域。
(4) 数据倾斜的预防
为了预防数据倾斜的发生,MR源码中定义了这样的代码while (((double) bytesRemaining)/splitSize > SPLIT_SLOP) { 切片时考虑 1.1倍溢出 防止数据倾斜问题。
具体来说,就是,每次切片时,都要判断切完剩下的部分是否大于块的1.1倍,不大于1.1倍就划分一块切片,大于的话就切出一个块的大小,然后再次对剩下的部分进行判断。
3. Reduce阶段 --- Partitioner
我们知道Hadoop 默认的分区器是基于 map 输出 key 的哈希值分区,这种分区方法仅在数据分布比较均匀时比较好,但在有数据倾斜时就很有问题。
那么在Reduce阶段数据倾斜又是怎么出现的呢?
对于分区,我们首先需要想明白的是,分区和ReduceTask之间的关系。我们举例说明:
假设自定义分区数为5,那么如果我的ReduceTask分别是下面的情况,就会产生不同的结果
- job.setNumRedceTasks(1) :程序正常运行,只不过会产生一个输出文件
- job.setNuRecuceTasks(2) :程序报错
- job.setNumRechceTasks(6) : 大于5,程序会正常运行,会产生空文件
所以程序中的 分区数 和 ReduceTask 的数量都是支持用户自定义的,但如果 Partitioner 和 ReduceTask的数量不匹配,就容易造成数据分布不均匀,导致数据倾斜。那么为了减缓 Reduce 阶段的数据倾斜问题,我们可以使用以下两种方法:
(1) 自定义分区
例如,如果map输出键的单词来源于一本书,其中大部分必然是省略词(stopword)。那么就可以使用自定义分区将这部分省略词发送给固定的一部分 reduce 实例,而将其他的都发送给剩余的 reduce 实例。但要注意的是,如果 ReduceTask的数量尽量保持和分区数一致,或稍微大于分区数,这个根据业务逻辑需求来考虑。
(2) Combine
使用 Combine 可以大量地减小数据频率倾斜和数据大小倾斜,因为 combine 的目的就是聚合并精简数据。