MapReduce流程及调优

流程原理

1 Map

1.1 map

根据输入,形成切片,并行运行MapTask。(MapTask并行度由切片数决定,每个切片对应一个MapTask)

1.2 partition

map()函数处理后得到的键值对在写入环形缓冲区前,需要先进行分区操作。(MR默认提供的分区类是HashPartitioner,可以通过继承Partitioner类并重新getPartition()方法来自定义分区)

分区数应该和ReduceTask数量一致。(可以通过job.setNumReduceTasks()设置,不设置的话默认只有一个ReduceTask,最终产生一个分区)

1.3 写入环形缓冲区

每个MapTask都会分配一个环形缓冲区,用来暂时存储map任务输出的键值对以及相应的partition信息。通过将键值对结果先写入缓冲区,避免频繁产生磁盘IO降低效率。

1.4 spill

当缓冲区内容达到阈值时(默认80%),就会锁定这80%的内存,并先根据分区排序,再在每个分区内对其中的键值对按key进行排序(快速排序)。(排序结果为缓冲区内的数据按partition为单位聚集在一起,同一partition内数据按key有序)

排序完成后会创建并写入一个临时的溢写文件。(如果自定义了Combiner,则会在分区排序后溢写前调用Combiner的reduce()函数)

缓冲区中剩余的20%内存在此阶段可以继续写入MapTask输出的键值对,写满时MapTask输出阻塞。

1.5 merge

当MapTask处理的数据量很大时会产生多个溢写文件,此时需要将同一个MapTask产生的多个溢写文件合并(归并排序),以最终形成一个已分区已排序的大文件。(如果自定义了Combiner,这个过程也会合并相同的键值的键值对)(在写磁盘的时候也可以采用压缩的方式将MapTask的输出结果进行压缩来减少网络开销,Reduce阶段在合并时会进行解压缩操作)

溢出文件合并完毕后,MapTask将删除所有的临时溢写文件,并告知NodeManager任务已完成,只要其中一个MapTask完成,ReduceTask就开始复制它的输出。

2 Reduce

2.1 copy

ReduceTask启动一些数据复制线程,通过HTTP方式请求MapTask所在的NodeManager以获取输出文件。ReduceTask通常需要集群上若干个MapTask的输出作为其接受分区的分区文件。而每个map任务的完成时间可能不同,因此只要有一个任务完成,ReduceTask就开始复制其输出。(ReduceTask的复制线程数默认为5,并行去取MapTask的输出)

2.2 merge

复制过来的数据会先放入内存缓冲区中,如果内存缓冲区中能放得下这次数据的话就直接把数据写到内存中。当内存缓存区中存储的Map数据占用空间达到一定程度的时候,则会把内存中的数据合并输出到磁盘文件中。与Map端的溢写类似,在将缓冲区中多个Map输出合并写入磁盘之前,如果设置了Combiner,则会通过reduce()函数合并Map输出。(Reduce的内存缓冲区可通过mapred.job.shuffle.input.buffer.percent配置,默认是JVM的heap size的70%)(将内存缓冲区的数据merge到磁盘文件的过程和copy过程同时运行,直到Map端的数据全部拷贝完成才结束)

当属于该Reducer的Map输出全部拷贝完成,则会在Reducer上生成多个文件(如果拉取复制的所有Map数据总量没有超过内存缓冲区大小,则数据只存在于内存中),然后执行合并操作(将已经各自排好序的Map输出文件根据键值进行归并排序),最终会输出一个整体有序的数据块。

2.3 reduce

当一个ReduceTask完成复制和排序后,就会针对已根据键值排序好的key构造对应的value迭代器。这时就要用到分组,默认根据键分组,也可以自定义分组函数类,并通过job.setGroupingComparatorClass()方法设置。对于默认分组来说,只要比较器比较的两个key值相等,它们就属于同一组,它们的value就会放在一个value迭代器,而这个迭代器的key使用属于同一个组的所有key的第一个key。

reduce()方法的输入是key和它的value迭代器。此阶段的输出直接写到输出文件系统,如果采用的文件系统是HDFS,由于NodeManager通常也运行数据节点,所以第一个块副本将被写到本地磁盘。ReduceTask为每个key和它的value迭代器 <key, (list of values)>对调用一次 reduce()方法,且最终的输出是没有排序的。

优化

其他

1 MR的shuffle和Spark的shuffle:

1.1 Spark的shuffle

shuffle的本质是将各个节点的同一类数据汇集到某一个节点进行计算。Spark中的shuffle操作
有hash shuffle和sort shuffle两种,而两种模式的主要差异是在shuffle write阶段(map端shuffle)       

1.1.1 hash shuffle

MR的shuffle将排序作为固定步骤,有许多并不需要排序的任务,MR也会对其进行排序,造成了不必要的开销。Spark基于hash的shuffle实现方式中,MapTask会为每个ReduceTask生成一个文件,这样容易产生大量文件。(即对应M*R个中间文件,其中, M表示Mapper阶段的Task个数,R表示 Reducer阶段的Task个数,伴随大量的随机磁盘I/O与内存开销)

为缓解该问题,Spark 0.8.1为hash shufflr引入了shuffle consolidate机制,合并Mapper端生成的中间文件。

1.1.2 sort shuffle

基于sort的shuffle中,每个MapTask不会为每个ReduceTask生成一个单独的文件,而是全部写到一个数据文件中,同时生成一个索引文件, ReduceTask通过索引文件获取相关的数据。最终生成的文件个数减少到 2*M。

  • 普通模式:
  • bypass模式:Reducer 端任务数比较少的情况下,hash shuffle的实现机制明显比sort shuffle 实现机制快,因此sort shuffle提供了一个hash风格的机制,就是 bypass模式。Reducer端任务数少于配置属性spark.shuffle.sort.bypassMergeThreshold设置的个数时,使用该模式。此时,每个task会为每个下游task都创建一个临时磁盘文件,并将数据按key进行hash然后根据 key的hash值,将key写入对应的磁盘文件中。当然,写入磁盘文件时也是先写入内存缓冲,缓冲写满之后再溢写到磁盘文件的。最后,同样会将所有临时磁盘文件都合并成一个磁盘文件,并创建一个单独的索引文件。该过程的磁盘写机制和未经优化的hash shuffle一模一样,都要创建大量磁盘文件,只是最后会做一个磁盘文件的合并。因此少量的最终磁盘文件,让该机制相对未经优化的hash shuffle来说,shuffle read的性能更好。而该模式与普通模式的不同在于:一是磁盘写机制不同;二是shuffle write过程中不会进行数据的排序操作,节省了排序的性能开销。
  • Tungsten Sort模式:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值