MapReduce的工作机制

Streaming运行特殊的map任务和reduce任务,目的是运行用户提供的可执行程序,并与之通信。

关于任务进度,对map任务,任务进度是已处理输入所占的比例,对于reduce任务,分为三个阶段与shuffle的三个阶段相对应。

关于shuffle和排序(shuffle是优化MapReduce非常重要的部分)

MapReduce确保每个reducer的输入是按键排序的。系统执行排序,将map输出作为输入传给reduce的过程成为shuffle。(从map产生输出到reduce消化输入的过程)

map函数有一个环形缓冲区,当缓冲区到达一定阈值之后,缓冲区开始向磁盘写入数据,在写入磁盘之前,线程会根据要传入的reduce函数将数据分为相应的分区(partition),在每个分区中按键排序,combiner函数在排序输出之后运行(如果有的话且能多次运行,若溢出文件超过三个,则会在写入磁盘之前再次调用combiner函数,不影响结果输出),使得map要传出的数据更加紧凑,减少要写入磁盘的数据和要传给reduce函数的数据。每次缓冲区达到阈值之后,会产生一个溢出文件,当map完成最后一个输入记录之后,会产生几个溢出文件,在任务完成之前,这些溢出文件会合并为一个已经分区且已经排序好的输出文件。

map函数的完成时间不同,在每一个map函数输出完成开始,reduce函数的复制线程就开始复制map函数的输出,且reduce函数有多个复制线程(默认是5个)可以并行复制。复制完所有的map输出之后,将开始合并阶段,这个阶段合并map输出,维持其顺序排序。例如一共有50个map输出,合并因子是10,那么合并之后会有5个中间文件。

最后阶段是reduce阶段,直接把数据输入reduce函数,从而省略了一次磁盘往返行程。最后的合并可以来自于内存和磁盘片段。在reduce阶段,对已排序输出的每个键调用reduce函数。此阶段的输出直接写到输出文件系统,一般为HDFS。如果采用HDFS,由于节点管理器也运行数据节点,所以第一个块复本也将写到本地磁盘。

shuffle配置调优原则上是给shuffle过程尽可能多提供内存空间。但是要确保map函数和reduce函数能够得到足够的内存来运行,所以写map函数和reduce函数的时候要尽量少使用内存,例如应避免在map函数中堆积数据。在map端可以通过避免多次溢出写磁盘来获得最佳性能,一次是最佳的情况。在reduce端,中间数据全部驻留在内存时,就能获得最佳性能。(默认情况下不可能发生)

任务的执行

map任务可以知道它处理的文件的名称,map任务和reduce任务可以得知任务的尝试次数。

MapReduce模型将作业分解成任务,然后并行的运行任务以使作业的整体执行时间少于各个任务顺序执行的时间。当一个任务执行比预期要慢的时候,会尽量检测,并启动另一个相同的任务作为备份,这就是所谓的任务的“推测执行”。对相同的任务,调度器会仅仅启动运行速度明显低于平均水平的那一小部分任务的推测副本。若原任务在推测任务前完成,推测任务就被终止,如果推测任务先完成,那么原任务也会被终止。推测执行默认情况下是启用的。目的是减少作业执行时间,是以集群效率为代价的,所以在集群上更倾向于关闭推测执行。对于reduce任务,关闭推测执行是有益的,因为任意重复的reduce任务都必须将取得map输出作为最先的任务,这可能会大幅度的增加集群上的网络传输。

 

  • MapReduce的执行过程分析
  1. 从job提交到执行完成这个过程

(1)客户端提交任务

Client提交任务时HDFS根据目标文件的大小,了解要获取的数据的规模,然后形成任务分配的规划,形成规划文件job.split。然后把规划文件、jar、配置文件xml提交给yarn。

(2)启动appmaster

appmaster是本次job的主管,负责maptask和reducetask的启动、监控、协调管理工作。yarn找一个合适的服务器来启动appmaster,并把job.split、jar、xml交给它。

(3)启动maptask

Appmaster启动之后,根据固化文件job.split中的分片信息启动maptask,一个分片对应一个maptask。

分配maptask时,会尽量让maptask在目标数据所在的datanode上执行。

(4)执行maptask

Maptask会一行行地读目标文件,交给我们写的map程序,读一行就调一次map方法,map调用context.write把处理结果写出去,保存到本机的一个结果文件,这个文件的内容是分区且有序的。分区的作用就是定义哪些key在一组,一个分区对应一个reducer

(5)启动reducetask

Maptask都运行完成之后,appmaster再启动reducetask,maptask的结果中有几个分区就启动几个reducetask。

(6)执行reducetask

reducetask去读取maptask的结果文件中自己对应的那个分区数据,例如reducetask_01去读第一个分区中的数据。

reducetask把读到的数据按key组织好,传给reduce方法进行处理,处理结果写到指定的输出路径。

GroupingComparator组比较器

reducer对map传过来的数据按照key值分组是将key相同的放在一组,然后对每一组调用一次reduce,其中分组的操作就需要用到GroupingComparator,对key进行比较,相同的放在一组。

Parititioner分区,将数据按照相应的标准分区,几个分区就会有几个reducetask,每一个reducetask输出一个文件,不同分区的数据写入了不同的结果文件。

其中Partitioner是属于map端的,而GroupingComparator是属于reduce端的。 

文件的读取流程以及合并小文件

文件的读取由map负责,在前面的示意图中可以看到一个inputformat用来读取文件,然后以key value形式传递给map方法。

当要计算的目标文件中有大量的小文件时,分配任务和资源的开销要比实际的计算开销大,故需要先把一些小文件合并成大文件。此时需要我们自定义一个inputformat和RecordReader,RecordReader负责实现一次读取一个完整文件封装为key value,map接收到文件内容,然后以文件名为key,以文件内容为value,向外输出的格式要注意,要使用SequenceFileOutPutFormat用来输出对象。因为reduce收到的key value都是对象,而不是普通的文本,reduce默认的输出格式是TextOutputFormat,使用它的话,最终输出的内容就是对象ID,所以要使用SequenceFileOutPutFormat进行输出。

分组输出到多个文件

默认情况下,每个reducer写入一个文件,文件名由分区号命名,例如'part-r-00000‘,而MultipleOutputs可以用key作为文件名,例如'Order_0000001-r-00000‘。

map中处理每条数据,以‘订单id‘为key,reduce中只用MultipleOutputs进行输出,会自动以key为文件名,文件内容就是相同key的所有记录。

例如‘Order_0000001-r-00000的内容就是:

Order_0000001,Pdt_05,25.8

Order_0000001,Pdt_01,222.8

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值