MapReduce Shuffle详解2

shuffle过程包括Map端和Reduce端, 为了更加形象理解,我们先看一组图:



Map端包括分区,排序,合并

Reduce端包括 复制,归并 reduce

在Map端的shuffle过程是对Map的结果进行分区(partition)、排序(sort)和分割(spill),然后将属于同一个划分的输出合并在一起(merge)并写在硬盘上,同时按照不同的划分将结果发送给对应的Reduce(Map输出的划分与Reduce的对应关系由JobTracker确定)。Reduce端又会将各个Map送来的属于同一个划分的输出进行合并(merge),然后对merge的结果进行排序,最后交给Reduce处理。通俗的讲,就是对Map输出结果先进行分区(partition),如“aaa”经过Partitioner后返回0,也就是这对值应当交由第一个reducer来处理。接下来,需要将数据写入内存缓冲区中,缓冲区的作用是批量收集map结果,减少磁盘IO的影响。我们的key/value对以及Partition的结果都会被写入缓冲区。当然写入之前,key与value值都会被序列化成字节数组。这个内存缓冲区是有大小限制的,默认是100MB。当map task的输出结果很多时,需要在一定条件下将缓冲区中的数据临时写入磁盘,然后重新利用这块缓冲区。这个从内存往磁盘写数据的过程被称为Spill。Spill可以认为是一个包括Sort和Combiner(Combiner是可选的,用户如果定义就有)的过程。先进行sort可以把缓冲区中一段范围key的数据排在一起,(如果数据多的时候,多次刷新往内存缓冲区中写入的数据可能会有属于相同范围的key,也就是说,多个spill文件中可能会有统一范围的key,这就是需要下面Map端merge的原因),这里有点绕,具体的介绍可以看下面的详细过程,执行过sort之后,如果用户定义了combiner就会执行combine,然后执行merge操作,接着就是Reduce端。

详细过程如下:
Map端:

当Map输出内存缓冲区内容达到设定的阈值时,就要把缓冲区内容分割(spill)到磁盘中。但在分割的时候Map并不会阻止继续向缓冲区中写入结果,如果Map结果生成的速度快于写出速度,那么缓冲区会写满,这时Map任务必须等待,直到分割写出过程结束。其实Spill可以认为是一个包括Sort和Combiner(Combiner是可选的,用户如果定义就有)的过程。将缓冲区中的内容写出时会调用sortAndSpill函数,每被调用一次就会创建一个spill文件,然后按照key值对需要写出的数据进行sort,(如果用户作业配置了conbiner类,那么在写出过程中会先对sort的结果进行进一步的合并(combine),目的是为了让他Map的输出数据更加紧凑)。最后按照划分的顺序将所有要写出的结果写入这个溢写文件(spill文件)中。 每次溢写都会在磁盘上生成一个溢写文件,如果map的输出结果真的很大,就会有多次这样的溢写发生,磁盘上相应就会有多个溢写文件存在。当map task真正完成时,内存缓冲区中的数据也全部溢写到磁盘中形成一个溢写文件。最终磁盘中会至少有一个这样的溢写文件存在(如果map的输出结果很少,当map执行完成时,只会产生一个溢写文件),因为直接将每一个Map生成的众多spill文件交给Reduce处理不显示。所以在每个Map任务结束之后在Map的TaskTracker上还会执行合并操作(merge),这个操作的主要目的就是将Map生成的众多spill文件中的数据(此时每个spill中的数据已经经过sort过程,一段范围的key已经排在一起了)按照划分重新组织,针对指定的分区,从各个spill文件中拿出属于同一个分区的所有数据,然后将它们合并在一起,并写入一个已分区且已排序的Map输出文件中,(该过程的详细情况请参考mergeParts( )函数的代码)。待唯一的已分区且已排序的Map输出文件写入最后一条记录后,Map端的shuffle阶段就结束了。就进入了Reduce端的shuffle阶段。
Reduce端:

在Reduce端,shuffle阶段可以分为三个阶段:复制Map输出、排序合并(merge)和Reduce处理。
简单地说,reduce task在执行之前的工作就是不断地拉取当前job里每个map task的最终结果,然后对从不同地方拉取过来的数据不断地做merge,也最终形成一个文件作为reduce task的输入文件。
详细过程如下:
1. copy过程:简单地拉取数据。Reduce进程启动一些数据copy线程(Fetcher),通过HTTP方式请求map task所在的TaskTracker获取map task的输出文件。因为map task早已结束,这些文件就归TaskTracker管理在本地磁盘中。 
2.merge阶段。
这里的merge如map端的merge动作,只是数组中存放的是不同map端copy来的数值。Copy过来的数据会先放入内存缓冲区中。这里需要强调的是,merge有三种形式:1)内存到内存  2)内存到磁盘  3)磁盘到磁盘。默认情况下第一种形式不启用,让人比较困惑,是吧。当内存中的数据量到达一定阈值,就启动内存到磁盘的merge。与map 端类似,
这也是溢写的过程,这个过程中如果你设置有Combiner,也是会启用的,然后在磁盘中生成了众多的溢写文件。第二种merge方式一直在运行,直到没有map端的数据时才结束,然后启动第三种磁盘到磁盘的merge方式生成最终的那个文件。 
3. Reduce处理阶段
不断地merge后,最后会生成一个“最终文件”。为什么加引号?因为这个文件可能存在于磁盘上,也可能存在于内存中。对我们来说,当然希望它存放于内存中,直接作为Reducer的输入,但默认情况下,这个文件是存放于磁盘中的。当Reducer的输入文件已定,整个Shuffle才最终结束。然后就是Reducer执行,把结果放到HDFS上。

配置调优:
该配置调优方案主要是对以上Shuffle整个过程中涉及到的配置项按流程顺序一一呈现并给以调优建议。 
1. Map端 
1) io.sort.mb 
用于map输出排序的内存缓冲区大小 
类型:Int 
默认:100mb 
备注:如果能估算map输出大小,就可以合理设置该值来尽可能减少溢出写的次数,这对调优很有帮助。
2)io.sort.spill.percent 
map输出排序时的spill阀值(即使用比例达到该值时,将缓冲区中的内容spill 到磁盘) 
类型:float 
默认:0.80
3)io.sort.factor 
归并因子(归并时的最多合并的流数),map、reduce阶段都要用到 
类型:Int 
默认:10 
备注:将此值增加到100是很常见的。
4)min.num.spills.for.combine 
运行combiner所需的最少溢出写文件数(如果已指定combiner) 
类型:Int 
默认:3
5)mapred.compress.map.output 
map输出是否压缩 
类型:Boolean 
默认:false 
备注:如果map输出的数据量非常大,那么在写入磁盘时压缩数据往往是个很好的主意,因为这样会让写磁盘的速度更快,节约磁盘空间,并且减少传给reducer的数据量。
6)mapred.map.output.compression.codec 
用于map输出的压缩编解码器 
类型:Classname 
默认:org.apache.Hadoop.io.compress.DefaultCodec 
备注:推荐使用LZO压缩。Intel内部测试表明,相比未压缩,使用LZO压缩的 TeraSort作业,运行时间减少60%,且明显快于Zlib压缩。
7) tasktracker.http.threads 
每个tasktracker的工作线程数,用于将map输出到reducer。 
(注:这是集群范围的设置,不能由单个作业设置) 
类型:Int 
默认:40 
备注:tasktracker开http服务的线程数。用于reduce拉取map输出数据,大集群可以将其设为40~50。
  1. reduce端 
1)mapred.reduce.slowstart.completed.maps 
调用reduce之前,map必须完成的最少比例 
类型:float 
默认:0.05
2)mapred.reduce.parallel.copies 
reducer在copy阶段同时从mapper上拉取的文件数 
类型:int 
默认:5
3)mapred.job.shuffle.input.buffer.percent 
在shuffle的复制阶段,分配给map输出的缓冲区占堆空间的百分比 
类型:float 
默认:0.70
4)mapred.job.shuffle.merge.percent 
map输出缓冲区(由mapred.job.shuffle.input.buffer.percent定义)使用比例阀值,当达到此阀值,缓冲区中的数据将会被归并然后spill 到磁盘。 
类型:float 
默认:0.66
5)mapred.inmem.merge.threshold 
map输出缓冲区中文件数 
类型:int 
默认:1000 
备注:0或小于0的数意味着没有阀值限制,溢出写将有mapred.job.shuffle.merge.percent单独控制。
6)mapred.job.reduce.input.buffer.percent 
在reduce过程中,在内存中保存map输出的空间占整个堆空间的比例。 
类型:float 
默认:0.0 
备注:reduce阶段开始时,内存中的map输出大小不能大于该值。默认情况下,在reduce任务开始之前,所有的map输出都合并到磁盘上,以便为reducer提供尽可能多的内存。然而,如果reducer需要的内存较少,则可以增加此值来最小化访问磁盘的次数,以提高reduce性能。
3.性能调优补充 
相对于大批量的小文件,hadoop更合适处理少量的大文件。一个原因是FileInputFormat生成的InputSplit是一个文件或该文件的一部分。如果文件很小,并且文件数量很多,那么每次map任务只处理很少的输入数据,每次map操作都会造成额外的开销。

若泽大数据交流群:671914634
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值