MapReduce shuffle:
-
Map方法之后,Reduce方法之前的数据处理过程称之为Shuffle。
-
在shuffle之前,MapReduce通过InputFormat接口中的getSplit()方法,对要当前要处理的HDFS块数据进行逻辑上的分片(split)操作,进而确定MapTask的个数
// 分片不会超过blockSize的大小,取goalSize和minSize的最大值
finalSplitSize=max(minSize,min(goalSize,blockSize))
- 并通过getpartition方法获取分区(确定ReduceTask个数),这样就能把map任务处理的结果发送给指定的reducer去执行,从而达到负载均衡,避免数据倾斜。MapReduce提供默认的分区类(HashPartitioner),其核心代码如下:
public class HashPartitioner<K, V> extends Partitioner<K, V> {
/** Use {@link Object#hashCode()} to partition. */
public int getPartition(K key, V value,
int numReduceTasks) {
return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
}
}
-
每一个分片对应一个MapTask任务,MapTask收集map()方法输出的每一个分区的kv对,放到环形缓冲区(默认大小100M,我们公司调整成了200M)中,当写入的数据量达到预先设置的阙值后(mapreduce.map.io.sort.spill.percent,默认0.80,我们公司调整成了95%),便会启动溢写线程将缓冲区中的那部分数据溢出写到磁盘的临时文件中,在溢写前会进行分区,并根据key进行排序(快排),这里也可以进行value的合并操作(combine,可选操作),然后溢写出的文件会进入到mapreduce.cluster.local.dir属性指定的本地目录中。当整个map任务完成溢出写后,会对磁盘中这个map任务产生的所有溢写出来的临时文件进行归并(merge)操作生成最终的正式输出文件,此时的merge是将所有spill文件按分区进行归并排序,生成key和对应的value-list。在文件归并时,如果溢写文件数量超过参数min.num.spills.for.combine的值(默认为3)时,会多次归并排序操作,到这里map端shuffle过程就结束了,接下来等待reduce task来拉取数据。
-
对于reduce端的shuffle过程来说,reduce task在执行之前的工作就是不断地拉取当前job里每个map task对应分区的结果(并行取得map输出,mapreduce.reduce.shuffle.parallelcopies=5),然后对从不同地方拉取过来的数据在内存中缓存,如果内存够用会直接在内存中归并排序(Reduce的内存缓冲区可通过mapred.job.shuffle.input.buffer.percent配置,默认是JVM的heap size的70%),如果内存不够遍会进行归并排序并落盘(内存到磁盘merge的启动门限可以通过mapred.job.shuffle.merge.percent配置,默认是66%),将分区相同的数据合并成一个大文件后,排好序之后紧接着进行分组,分组完成后才将整个文件交给reduce方法处理。
-
Combiner:如果指定了Combiner,可能在两个地方被调用:
1.当为作业设置Combiner类后,缓存溢出线程将缓存存放到磁盘时,就会调用;
2.缓存溢出的数量超过mapreduce.map.combine.minspills(默认3)时,在缓存溢出文件合并的时候会调用 -
合并(Combine)和归并(Merge)的区别:
两个键值对<“a”,1>和<“a”,1>,如果合并,会得到<“a”,2>,如果归并,会得到<“a”,<1,1>> -
归并merge:溢出写文件归并完毕后,Map将删除所有的临时溢出写文件,并告知NodeManager任务已完成,只要其中一个MapTask完成,ReduceTask就开始复制它的输出(Copy阶段分区输出文件通过http的方式提供给reducer)
-
压缩:写磁盘时压缩map端的输出,因为这样会让写磁盘的速度更快,节约磁盘空间,并减少传给reducer的数据量(大部分情况下分配的container是在不同节点,需要网络传输)。默认情况下,输出是不压缩的(将mapreduce.map.output.compress设置为true即可启动)
-
Reduce的内存缓冲区可通过mapred.job.shuffle.input.buffer.percent配置,默认是JVM的heap size的70%。内存到磁盘merge的启动门限可以通过mapred.job.shuffle.merge.percent配置,默认是66%。
用博客见证成长,用行动证明我在努力。
如果你有缘看到我博客,对你有帮助、喜欢博客内容,请“点赞” “评论”“收藏”
一键三连哦!