MapReduce2原理解析

1 MapReduce概述

      MapReduce源自于Google的MapReduce论文,该论文发表于2004年12月,现在的Hadoop MapReduce是Google MapReduce的克隆版本。
      MapReduce的特点:①易于编程:用户不用考虑进程间的通信和套接字编程;②良好的扩展性:当集群资源不能满足计算需求时,可以以增加节点的方式达到线性扩展集群的目的;③高容错性:对于节点故障导致失败的作业,MapReduce计算框架会自动地讲作业安排到健康的节点进行,直到任务完成;④适合PB级以上海量数据的离线处理
      然而,MapReduce不擅长的方面:①实时计算:在毫秒级或秒级内返回结果;②流式计算:MapReduce的输入数据集是静态的,不能动态变化;③DAG(Directed Acyclical Graphs)计算:多个应用程序存在依赖关系,后一个应用程序的输入作为前一个的输出。

2 MapReduce1框架的缺陷

      在Hadoop1.X的时代,MapReduce做了很多的事情,其核心是JobTracker。

2.1 MapReduce1架构 在这里插入图片描述

      1)首先客户端要编写好MapReduce程序,然后提交作业(job),job的信息会发送JobTracker上。
      2)JobTracker为该job分配一个ID值,接下来JobTracker做检查操作,确认输入目录是否存在,如果不存在,则会抛错,如果存在继续检查输出目录是否存在,如果存在则会抛错,否则继续执行;当检查工作都做好了JobTracker就会配置Job需要的资源了。
      3)TaskTracker主动与JobTracker通信,接收作业。
      4)TaskTracker执行每一个任务。
(1)JobTracker
      主要负责资源监控管理和作业调度。
      ①监控所有TaskTracker 与job的健康状况,一旦发现失败,就将相应的任务转移到其他节点;
      ②同时JobTracker会跟踪任务的执行进度、资源使用量等信息,并将这些信息告诉任务调度器,而调度器会在资源出现空闲时,选择合适的任务使用这些资源。
(2)TaskTracker
      JobTracker与Task之前的桥梁。
      ①从JobTracker接收并执行各种命令:运行任务、提交任务、Kill任务、重新初始化任务;
      ②周期性地通过心跳机制,将节点健康情况和资源使用情况、各个任务的进度和状态等汇报给JobTracker。
(3)Task Scheduler
      任务调度器(默认FIFO,先按照作业的优先级高低,再按照到达时间的先后选择被执行的作业)。
(4)Map Task
      ①Map引擎;
      ②解析每条数据记录,传递给用户编写的map();
      ③将map()输出数据写入本地磁盘(如果是map-only作业,则直接写入HDFS)。
(5)Reduce Task
      ①Reduce引擎;
      ②从Map Task上远程读取输入数据;
      ③对数据排序;
      ④将数据按照分组传递给用户编写的reduce()。

2.2 MapReduce缺陷

      Hadoop1.x的MapReduce框架的主要局限
      (1)JobTracker 是 Map-reduce 的集中处理点,存在单点故障,可靠性差
      (2)JobTracker 完成了太多的任务,造成了过多的资源消耗,当 map-reduce job 非常多的时候,会造成很大的内存开销,潜在来说,也增加了 JobTracker 失效的风险,这也是业界普遍总结出老 Hadoop 的 Map-Reduce 只能支持 4000 节点主机的上限,扩展性能差
      (3)可预测的延迟:这是用户非常关心的。小作业应该尽可能快地被调度,而当前基于TaskTracker->JobTracker ping(heartbeat)的通信方式代价和延迟过大,比较好的方式是JobTracker->TaskTracker ping, 这样JobTracker可以主动扫描有作业运行的TaskTracker。
      综上所述,MapReduce1已经不能满足我们的需求,因此,hadoop2.x中MapReduce2应运而生。

3 MapReduce2工作机制

3.1 MapReduce2架构 在这里插入图片描述

(1)作业提交
      Client调用job.waitForCompletion方法,向整个集群提交MapReduce作业(第1步)。新的作业ID(应用ID)由ResourceManager分配(第2步)。作业的client核实作业的输出,计算输入的split,将作业的资源(包括jar包、配置文件和split信息等)拷贝给HDFS(第3步)。最后,通过调用ResourceManager的submitApplication()来提交作业(第4步)。
(2)作业初始化
      当ResourceManager收到submitApplication()的请求时, 就将该请求发给调度器(scheduler), 调度器分配container, 然后ResourceManager在该container内启动ApplicationMaster进程, 由NodeManager监控(第5a和5b步)。
      MapReduce作业的ApplicationMaster是一个主类为MRAppMaster的Java应用。其通过创造一些bookkeeping对象来监控作业的进度, 得到任务的进度和完成报告(第6步)。MRAppMaster通过分布式文件系统得到由客户端计算好的输入split(第7步)。MRAppMaster为每个输入split创建一个map任务, 根据mapreduce.job.reduces创建reduce任务对象。
(3)任务分配
      如果作业很小,ApplicationMaster会选择在其自己的JVM中运行任务。如果不是小作业,那么ApplicationMaster向ResourceManager请求container来运行所有的map和reduce任务(第8步)。这些请求是通过心跳来传输的,包括每个map任务的数据位置,比如存放输入split的主机名和机架(rack)。调度器利用这些信息来调度任务,尽量将任务分配给存储数据的节点,或者退而分配给和存放输入split的节点相同机架的节点。
(4)任务运行
      当一个任务由ResourceManager的调度分配给一个container后, ApplicationMaster通过联系NodeManager来启动container(第9a步和9b步)。任务由一个主类为YarnChild的Java应用执行。在运行任务之前首先本地化任务需要的资源,比如作业配置、JAR文件和分布式缓存的所有文件(第10步)。最后,运行map或reduce任务(第11步)。
      YarnChild运行在一个专用的JVM中, 但是YARN不支持JVM重用。
(5)进度和状态更新
      YARN中的任务将其进度和状态(包括counter)返回给ApplicationMaster,客户端每秒轮询(通过mapreduce.client.progressmonitor.pollinterval设置)向ApplicationMaster请求进度更新,展示给用户。
(6)作业完成
      除了向ApplicationMaster请求作业进度外,客户端每5分钟都会通过调用waitForCompletion()来检查作业是否完成。时间间隔可以通过mapreduce.client.completion. pollinterval来设置。作业完成之后, ApplicationMaster和container会清理工作状态, OutputCommiter的作业清理方法也会被调用。作业的信息会被作业历史服务器存储以备之后用户核查。最后,ApplicationMaster向ResourceManager注销并关闭自己。

3.2 失败
3.2.1 任务运行失败

(1)任务失败标记
      ① map 任务或reduce 任务中的用户代码抛出运行异常。
      任务JVM会在退出之前向其父ApplicationMaster发送错误报告。错误报告最后被记入用户日志。ApplicationMaster将此次任务尝试标记为failed)(失败),并释放container以便资源可以被其他任务使用。
      ② 任务JVM突然退出,例如JVM的bug。
      NodeManager会注意到进程已经退出,并通知ApplicationMaster将此次任务尝试标记为失败。
      ③任务超时
      ApplicationMaster每隔一段时间接收各个任务的汇报,一旦出现任务没有汇报,则ApplicationMaster就会将该任务标记为失败。
      任务超时的时间默认为10分钟,也可以在作业中通过mapreduce.task.timeout选项来为每个作业单独配置。设置为0表示无任务超时时间,此时任务运行再久也不会被标记为失败,其资源也无法释放,会导致集群效率降低。
(2)失败任务的重启
      当ApplicationMaster注意到一个任务失败了之后,将会尝试重新调度该任务的执行,但不会在之前失败了的节点上执行,并且失败四次之后ApplicationMaster将不会继续重启该任务,失败次数是可以配置的:①map失败的最大次数:mapreduce.map.maxattempts;②reduce失败的最大次数:mapreduce.reduce.maxattempts。被终止的任务尝试不会记入任务运行尝试次数,因为尝试被终止不是任务的过错。(若是推测副本或者它所处的NodeManager失败,导致ApplicationMaster将它上面运行的所有任务尝试标记为killed)
      对于一些应用程序,我们不希望一旦有少数几个任务失败就中止运行整个作业,因为即使有任务失败,作业的一些结果可能还是可用的。在这种情况下,可以为作业设置在不触发作业失败的情况下允许任务失败的最大百分比:①mapreduce.map.failures.maxpercent;②mapreduce.reduce.failures.maxpercent。

3.2.2 ApplicationMaster失败

      就像MapReduce任务在遇到硬件或网络故障时要进行几次尝试一样。运行MapReduce ApplicationMaster的最多尝试次数由mapreduce.am.max-attempts 属性控制,默认值是2。YARN 对集群上运行的YARN ApplicationMaster的最大尝试次数加以了限制,单个的应用程序不可以超过这个限制。该限制由yarn.resourcemanager.am.maxattempts属性设置,默认值是2,这样如果你想增加MapReduce ApplicationMaster的尝试次数,你也必须增加集群上YARN 的设置。
      由于ApplicationMaster通过心跳机制向ResourceManager信息,当ResourceManager意识到ApplicationMaster失败了之后,会在另外一个节点的Container上重启。重启的ApplicationMaster使用作业历史来恢复失败的应用程序所运行任务的状态,使其不必重新运行,任务进度恢复默认是开启的,可以通过yarn.app.mapreduce.am.job.recovery.enable为false来禁用。
      客户端通过ApplicationMaster来获得作业的执行情况,当ApplicationMaster失效的时候,客户端会重新向ResourceManager请求新的ApplicationMaster地址来更新信息。

3.2.3NodeManager失败

      NodeManager通过心跳机制向ResourceManager汇报情况,当一个NodeManager失效,或者运行缓慢的时候,ResourceManager将收不到该NodeManager的心跳,或者心跳时间超时,此时ResourceManager会认为该NodeManager失败并移出可用NodeManager管理池,心跳超时的时间通过yarn.resourcemanager.nm.liveness-monitor.expiry-interval-ms来配置,默认为10分钟。
      在失败的NodeManager上运行的ApplicationMaster或者任务(即使完成也要重启,原因是这些任务的中间结果存储在失败的NodeManager的本地文件系统中,可能无法被reduce任务访问)都会按照前两节描述的机制进行恢复。
      Yarn对于NodeManager的管理还有一个类似黑名单的功能,当该NodeManager上的任务失败次数超过3次之后(默认),该NodeManager会被拉入黑名单此时,即使该NodeManager没有失效,ApplicationMaster也不会在该NodeManager上运行任务了。NodeManager上的最大任务失败次数可以通过mapreduce.job.maxtaskfailures.per.tracker来配置。

3.2.4 ResourceManager失败

      使用ZooKeeper实现了高可用,ResourceManager分为主机和备机。ResourceManager从备机切换到主机是由故障转移控制器(failover controller)处理的。使用ZooKeeper的leader选举机制(leader election)以确保同一时刻只有一个主ResourceManager。
      为应对资源管理器的故障转移,必须对客户和NodeManager进行配置,因为他们可能是在和两个ResourceManager打交道。客户和节点管理器以轮询(round-robin)方式试图连接每一个ResourceManager,直到找到主ResourceManager。如果主资源管理器故障, 他们将再次尝试直到备份资源管理器变成主机。

3.3 MapReduce编程模型

(1)内部逻辑
在这里插入图片描述
(2)外部物理结构
在这里插入图片描述
      Map阶段由一定数量的Map Task组成。包括:①输入数据格式分析:InputFormat;②输入数据处理:Mapper;③map输出数据的组合:Combiner;④map输出数据的分组:Partitioner。
(1)InputFormat
      文件分片(InputSplit)方法,用来处理跨行问题。
      将分片数据解析成key/value对,默认实现是TextInputFormat。TextInputFormat:①Key是行在文件中的偏移量,value是行内容;②若行被截断,则读取下一个block的前几个字符。TextlnputFormat 的逻辑记录和HDFS 块如下图所示:
在这里插入图片描述
      一个文件分成几行,行的边界与HDFS 块的边界没有对齐。分片的边界与逻辑记录的边界对齐(这里是行边界),所以第一个分片包含第5 行,即使第5 行跨第一块和第二块。第二个分片从第6 行开始。
      块是数据存储的最小单元,片是MapReduce的最小计算单元,为了更好的利用数据本地化优势,通常将片和块一一对应。当然,快和片的对应关系也可由用户控制。
      数据本地性:如果任务运行在它将处理的数据所在的节点,则称该任务具有“数据本地性”。本地性可避免跨节点或机架数据传输,提高运行效率。数据本地性分类:①同节点;②同机架;③不同机架。
(2)Partitioner
      Partitioner决定了Map Task输出的每条数据交给哪个Reduce Task处理。默认实现:hash(key) mod R,其中R是Reduce Task数目。允许用户自定义。
      很多情况需自定义Partitioner,比如“hash(hostname(URL)) mod R”确保相同域名的网页交给同一个Reduce Task处理。
(3)Combiner
      可以理解为local reducer:通常与Reducer逻辑一样,合并相同的key对应的value。经过Partitioner排序后,如果作业中配置了Combiner,就会调用Combiner,Combiner就好像在Mapper端提前进行一下Reducer一样。
      好处:①减少Map Task输出数据量(磁盘IO);②减少Reduce-Map网络传输数据量(网络IO)。
      然而,在一些场景Combiner是不能使用的,例如求平均值。

3.4 Map Task工作机制 在这里插入图片描述

      (1)首先,读取数据组件 InputFormat(默认TextInputFormat)会通过getSplits方法对输入目录中文件进行逻辑切片规划得到splits,有多少个split就对应启动多少个MapTask。split与block的对应关系可能是一对多,默认是一对一。
      (2)将输入文件切分为splits之后,由RecordReader对象(默认LineRecordReader)进行读取,以"\n"作为分隔符,读取一行数据返回<key,value>。key表示每行首字符偏移值,value表示这一行文本内容。
      (3)读取split返回<key,value>,进入用户自己继承的Mapper类中,执行用户重写的map函数。RecordReader 读取一行这里调用一次。
      (4)map逻辑完之后,将map的每条结果通过context.write进行collect收集。在collect中,会先对其进行分区处理,默认使用HashPartitioner。
      【MapReduce提供Partitioner接口,它的作用就是根据key或value及reduce的数量来决定当前的这对输出数据最终应该交由哪个reduce task处理。默认对key hash之后再以reduce task 数量取模。默认的取模方式只是为了平均reduce的处理能力,如果用户自己对Partitioner有需求,可以订制并设置到job上。】
      (5)接下来,会将数据写入内存,内存中这片区域叫做环形缓冲区,缓冲区的作用是批量收集 map结果,减少磁盘IO影响。我们的 key/value 对以及Partition 的结果都会被写入缓冲区。当然写入之前,key 与 value 值都会被序列化成字节数组。
      【环形缓冲区其实是一个数组,数组中存放着 key、value 的序列化数据和 key、value 的元数据信息,包括 partition、key 的起始位置、value 的起始位置以及 value 的长度。环形结构是一个抽象概念。缓冲区是有大小限制,默认是 100MB。当Map Task的输出结果很多时,就可能会撑爆内存,所以需要在一定条件下将缓冲区中的数据临时写入磁盘,然后重新利用这块缓冲区。这个从内存往磁盘写数据的过程被称为Spill(溢写)。这个溢写是由单独线程来完成,不影响往缓冲区写map结果的线程。溢写线程启动时不应该阻止map的结果输出,所以整个缓冲区有个溢写的比例spill.percent。这个比例默认是 0.8,也就是当缓冲区的数据已经达到阈值(buffer size * spill percent = 100MB * 0.8 = 80MB),溢写线程启动,锁定这 80MB 的内存,执行溢写过程。Map task的输出结果还可以往剩下的 20MB 内存中写,互不影响。】
      (6)当溢写程序启动后,需要对这 80MB 空间内的 key 做排序(Sort)。排序是MapReduce 模型默认的行为,这里的排序也是对序列化的字节做的排序。如果 job 设置过Combiner,那么现在就是使用Combiner的时候了。将有相同 key 的key/value对的value加起来,减少溢写到磁盘的数据量。Combiner会优化MapReduce的中间结果,所以它在整个模型中会多次使用。
      (7)每次溢写会在磁盘上生成一个临时文件(写之前判断是否有 combiner),如果 map 的输出结果真的很大,有多次这样的溢写发生,磁盘上相应的就会有多个临时文件存在。当整个数据处理结束之后开始对磁盘中的临时文件进行merge 合并,因为最终的文件只有一个,写入磁盘,并且为这个文件提供了一个索引文件,以记录每个 reduce 对应数据的偏移量。至此 map 整个阶段结束。

3.5 Reduce Task工作机制 在这里插入图片描述

      (1)Copy阶段:简单地拉取数据。Reduce进程启动一些数据copy线程(Fetcher),通过HTTP方式请求Map Task获取属于自己的文件。
      (2)Merge阶段。这里的merge如map端的merge动作,只是数组中存放的是不同map端copy来的数值。Copy过来的数据会先放入内存缓冲区中,这里的缓冲区大小要比map端的更为灵活。merge有三种形式:内存到内存;内存到磁盘;磁盘到磁盘。默认情况下第一种形式不启用。当内存中的数据量到达一定阈值,就启动内存到磁盘的 merge。与map端类似,这也是溢写的过程,这个过程中如果你设置有Combiner,也是会启用的,然后在磁盘中生成了众多的溢写文件。第二种merge方式一直在运行,直到没有map端的数据时才结束,然后启动第三种磁盘到磁盘的merge方式生成最终的文件。
      (3)把分散的数据合并成一个大的数据后,还会再对合并后的数据排序。
      (4)对排序后的键值对调用reduce方法,键相等的键值对调用一次reduce方法,每次调用会产生零个或者多个键值对,最后把这些输出的键值对写入到HDFS文件中。

3.6 Shuffle和排序

      Shuffle 是 MapReduce 的核心,它分布在 MapReduce 的Map 阶段和Reduce阶段。一般把从 Map产生输出开始到Reduce 取得数据 作为输入之前的过程称作Shuffle。 在这里插入图片描述
      (1)Collect 阶段:将 Map Task的结果输出到默认大小为 100M的环形缓冲区,保存的是 key/value,Partition 分区信息等。
      (2)Spill 阶段:当内存中的数据量达到一定的阀值的时候,就会将数据写入本地磁盘,在将数据写入磁盘之前需要对数据进行一次排序的操作,如果配置了Combiner,还会将有相同分区号和 key 的数据进行排序。
      (3)Merge 阶段:把所有溢出的临时文件进行一次合并操作,以确保一个Map Task最终只产生一个中间数据文件。
      (4)Copy 阶段: Reduce Task 启动 Fetcher 线程到已经完成 Map Task 的节点上复制一份属于自己的数据,这些数据默认会保存在内存的缓冲区中,当内存的缓冲区达到一定的阀值的时候,就会将数据写到磁盘之上。
      (5)Merge 阶段:在 Reduce Task 远程复制数据的同时,会在后台开启两个线程对内存到本地的数据文件进行合并操作。
      (6)Sort 阶段:在对数据进行合并的同时,会进行排序操作,由于 Map Task阶段已经对数据进行了局部的排序,Reduce Task 只需保证 Copy 的数据的最终整体有效性即可。Shuffle 中的缓冲区大小会影响到MapReduce 程序的执行效率,原则上说,缓冲区越大,磁盘 io 的次数越少,执行速度就越快。缓冲区的大小可以通过参数调整, 参数:io.sort.mb 默认 100M。
      Merge优化措施:目标是合并最小数量的文件以便满足最后一趟的合并系数。因此如果有40 个文件,我们不会在四趟中每趟合并10 个文件从而得到4 个文件。相反,第一趟只合并4 个文件,随后的三趟合并完整的10 个文件。在最后一趟中, 4 个已合井的文件和余下的6 个(未合并的)文件合计10 个文件。
在这里插入图片描述
      这并没有改变合并次数,它只是一个优化措施,目的是尽量减少写到磁盘的数据量,因为最后一趟总是直接合并到reduce。

4 总结

      不要轻易使用MapReduce,当出现以下情况时,它的效率是很低的:①整个文件可以加载到内存中(单机就能完成);②文件太大不能加载到内存中,但<word, count>可以存放到内存中(一条记录一条记录地读,map处理后将<word, count>放到内存中,同样也是单机就能完成)。
      当文件太大无法加载到内存中,且<word, count>也不行时,这时候采用MapReduce效率就会提高了。

参考文章:
[1]《Hadoop权威指南》
[2] https://blog.csdn.net/weixin_41668549/article/details/83421867
[3] https://blog.csdn.net/yu0_zhang0/article/details/78907178
[4] https://blog.csdn.net/u012599988/article/details/46860307
[5] https://www.cnblogs.com/zimo-jing/p/8846569.html
[6] https://www.jianshu.com/p/1b2508301f57
[7] https://www.cnblogs.com/likemebee/p/hadoop.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值