目录
1.shuffle是什么?
shuffle是贯穿MapReduce中Map\Reduce阶段的一个操作,主要目的是为了把Map端的输出结果有效的传输到Reduce输入端
2.shuffle做什么?
- 完整的将Map端的输出结果拉取到Reduce端
- 在拉取数据的过程中,尽可能的减少网络传输的消耗
- 尽可能的减少磁盘IO对Task执行效率的影响
3.shuffle怎么做?
(1)在Map端,shuffle分为四个步骤
- split过程:对输入文件进行split切分操作,切片的策略如下:
InputSplit = max(minSize,min(maxSize,blockSize))
其中,在hadoop2.x下blockSize默认为128M,minSize和maxSize的值分别可以通过api设置
minSize:TextInputFormat.setMinInputSplitSize(job,1024L) 设置最小值为1KB
maxSize:TextInputFormat.setMaxInputSplitSize(job,1024*1024*10L) 设置最大值为10M
由此可以看出,split和block有如下关系:
a.当blockSize < maxSize && blockSize > minSize时,splitSize = blockSize(一对一)
b.当blockSize < maxSize && blockSize < minSize时,splitSize = minSize
c.当blockSize > maxSize && maxSize > minSize时,splitSize = maxSize(多对一)
所以一般情况下,split与block之间是一一对应的。
在文件大小保持不变,blockSize默认大小保持不变的情况下,有如下两种情况:
1.输入文件巨大,且都是大文件(注意:这里的大文件指的是文件大小均大于等于blockSize)
a.为了增加map Task的数量,只需要增大minSize的大小
2.输入文件巨大,且都是小文件(注意:这里的小文件指的是文件大小均小于blockSize)
- partition过程:运行mapper之后得到的key/value的输出结果文件,但是这些结果文件不知道接下来要交给哪个reduce来处理,所以通过partition进行划分
MapReduce提供了partition接口,默认操作是对Key哈希后,再与reduce的数量取模,返回值决定该key/value由哪个reduce处理,这样的做法是为了防止数据倾斜,负载均衡
- 写入缓冲区和溢写过程:在对map的输出结果进行partition后,会将map的输出结果和partition结果序列化后,一同写入内存缓冲区中。每一个map都有一个相应的内存缓冲区,该缓冲区默认大小为100M(mapreduce.task.io.sort.mb调整)。当内存缓冲区的占用率达到阈值时(mapreduce.map.io.sort.spill.percent,默认0.80,或者80%),会锁定着80%的内存,并将该内存内容溢写到磁盘中。
在将数据溢写到磁盘之前,会先对这80%的内容进行sort(先按照partition排序,再根据key字典排序),即将相同partition的数据归为一组,然后再对相同partition下的数据进行key的字典排序。
在此过程中,如果客户端自定义了Combiner(相当于mapReduce的reduce操作),会对同一partition下的同一个key的value进行合并,之后再溢写到磁盘中
与此同时,内存缓冲区剩余20%的空间会继续写入map输出结果,溢写过程和写入缓冲区过程两者之间互不影响
- 归并过程:当输入的是一个大文件时,split之后每个map对应的split切片再经过溢写过程后会生成多个临时文件,这时候需要对溢写到磁盘的多个临时文件进行归并操作,合成一个已分区且已排序的大文件
这个过程中,又会对map输出结果进行一次sort排序(先按照partition排序,再根据key字典排序),与此同时,若客户端设置过Combiner,又会对同一partition下的同一个key的value进行合并。
当所有的临时文件都归并成一个大文件后,会删除所有的临时文件,并通知TaskTracker任务已完成,一旦有一个Map Task完成,Reduce Task就会通过Http复制Map Task的输出结果
合并Combiner和归并Merge的区别:两个键值对<"a",1>和<"a",1>,如果合并,会得到<"a",2>,如果归并,会得到<"a",<1,1>>
(2)在Reduce端,shuffle分为二个步骤
- copy过程:通过Http方式获取map阶段的输出结果文件。
当Map Task完成后,会通知Map Task所在的TaskTracker状态已更新,然后TaskTracker会通过心跳机制向JobTracker汇报执行情况。所以一般情况下,JobTracker会有Map Task与TaskTracker的映射关系,reduce会定期向JobTracker获取Map Task的输出位置,一旦知道Map Task有输出结果,就会通过Map Task对应的TaskTracker复制结果文件。(并非需要等待所有的Map Task都执行完毕之后才开始copy数据,而是只要有就开始copy)
- 写入缓冲区和merge sort过程:从Map端获取的输入文件会写入到内存缓冲区(该内存缓冲区不同于Map端的,使用的是Java Heap)
Copy过来的数据会先放入内存缓冲区中,如果内存缓冲区中能放得下这次数据的话就直接把数据写到内存中,即内存到内存
Reduce要向每个Map去拖取数据,在内存中每个Map对应一块数据,当内存缓存区中存储的Map数据占用空间达到一定程度的时候,开始启动内存中merge,把内存中的数据merge输出到磁盘上一个文件中,即内存到磁盘
在将buffer中多个map输出合并写入磁盘之前,如果设置了Combiner,则会化简压缩合并的map输出。Reduce的内存缓冲区可通过mapred.job.shuffle.input.buffer.percent配置,默认是JVM的heap size的70%。内存到磁盘merge的启动门限可以通过mapred.job.shuffle.merge.percent配置,默认是66%。
当属于该reducer的map输出全部拷贝完成,则会在reducer上生成多个文件(若拖取的所有map数据总量没有超过缓冲区的阈值,则数据就只存在于内存中),这时开始执行合并操作,即磁盘到磁盘
Map的输出数据已经是有序的,Merge进行一次合并排序(对key进行归并排序),所谓Reduce端的sort过程就是这个合并的过程。一般Reduce是一边copy一边sort,即copy和sort两个阶段是重叠而不是完全分开的。最终Reduce shuffle过程会输出一个整体有序的数据块,此时整个shuffle过程结束。
(3)对于Map端的输出结果和Reduce端的输出结果,都可以通过设置进行文件的压缩,从而减少网络传输的消耗
- map的压缩输出
conf.setBoolean("mapred.compress.map.out", true);
conf.setClass(Job.MAP_OUTPUT_COMPRESS_CODEC, GzipCodec.class, CompressionCodec.class);
- reduce的压缩输出
conf.setBoolean("mapred.output.compress", true);
以上是我查阅许多博主对shuffle的理解后整理出来的,若有不对欢迎指正和探讨!
参考文献

467

被折叠的 条评论
为什么被折叠?



