MapReduce运行机制

    Hadoop中的MapReduce是一种用于并行处理大量数据集的基于YARN的系统,是一个使用简单的软件框架,基于它写出来的应用程序能够运行在由上千个商用机器组成的大型集群上,并以一种可靠容错式并行处理TB级别的数据集。

    一个MapReduce作业(Job)通常会把输入的数据集切片分为若干独立的数据块,由Map任务以完成并行的方式处理它们。框架会对map函数的输出先进行排序,然后把结果输入给Reduce任务。通常作业的输入和输出都会被存储在文件系统中。整个框架负责任务的调度和监控,以及重新执行已经失败的任务。

    通常MapReduce框架和分布式文件系统是运行在一组相同的节点上的,也就是说,计算节点和存储节点通常在一起。这种配置允许在那些已经存好数据的节点上该校的调度任务,这可以使整个集群的网络带宽被非常高效地利用。

从客户端、JobTracker、TaskTracker层次分析MapReduce工作原理


    第一步:首先在客户端启动一个作业

    第二布:向JobTracker请求一个Job ID

    第三步:运行作业所需要的资源文件复制到HDFS上,包括MapReduce程序打包的JAR文件、配置文件和客户端计算所得的输入划分信息。这些文件都存放在JobTracker专门为该作业创建的以该作业的Job ID命名的文件夹中。JAR文件默认会有10个副本(mapred.submit.replication属性控制);输入划分信息告诉了JobTracker应该为这个作业启动多少个map任务等信息。

    第四步:等JobTracker接收到作业后,将其放在一个作业队列里,等待作业调度器对其进行调度,当作业调度器根据自己的调度算法调度到该作业时,会根据输入划分信息为每个划分创建一个map任务,并将map任务分配给TaskTracker执行。对于map和reduce任务,TaskTracker根据主机核的数量和内存的大小分配固定数量的map槽和reduce槽。这里需要强调的是:map任务不是随随便便地分配给某个TaskTracker的, 这里有个概念叫:数据本地化(Data-Local)。意思是:将map任务分配给含有该map处理的数据块的TaskTracker上,同时将程序JAR包复制到该TaskTracker上来运行,这叫“运算移动,数据不移动”。而分配reduce任务时并不考虑数据本地化。

    第五步:TaskTracker每隔一段时间会给JobTracker发送一个心跳,告诉JobTracker它依然在运行,同时心跳中还携带着很多的信息,比如当前map任务完成的进度等信息。当JobTracker收到作业的最后一个任务完成信息时,便把该作业设置成“成功”。当JobClient查询状态时,它将得知任务已完成,便显示一条消息给用户。

工作流程概述

MapReduce 的核心思想可以用“分而治之”来描述:


    一个大的MapReduce 作业,首先会被拆分成许多个 Map 任务在多台机器上并行执行,每个 Map 任务通常运行在数据存储的节点上,这样,计算和数据就可以放在一起运行,不需要额外的数据传输开销。当 Map 任务结束后,会生成以<key,value>对表示的许多中间结果。然后,这些中间结果会被分发到多个 Reduce任务在多台机器上并行执行,具有相同 key 的<key,value>发送到同一个 Reduce 任务那里,Reduce 任务会对中间结果进行汇总计算得到最后结果,并输出到分布式文件系统中。

需要指出的是:

    不同的 Map 任务之间不会进行通信

    不同的 Reduce 任务之间也不会发生任何信息交换;

    用户不能显式地从一台机器向另一台机器发送消息,

    所有的数据交换都是通过 MapReduce 框架自身去实现的。

    在 MapReduce 的整个执行过程中,Map 任务的输入文件、Reduce 任务的处理结果都是保存在分布式文件系统中的,而 Map 任务处理得到的中间结果则保存在本地存储中(如磁盘)

    只有当 Map处理全部结束后,Reduce 过程才能开始

    只有 Map 需要考虑数据局部性,实现“计算向数据靠拢”,而 Reduce 则无需考虑数据局部性。

MapReduce 的各个执行阶段

(1) MapReduce 框架使用 InputFormat 模块做 Map 前的预处理,比如验证输入的格式是否符合输入定义;然后,将输入文件切分为逻辑上的多个 InputSplit,InputSplit 是 MapReduce 对文件进行处理和运算的输入单位,只是一个逻辑概念,每个 InputSplit 并没有对文件进行实际切割,只是记录了要处理的数据的位置和长度

(2) 因为 InputSplit 是逻辑切分而非物理切分,所以还需要通过 RecordReader(RR)根据 InputSplit中的信息来处理 InputSplit 中的具体记录,加载数据并转换为适合 Map 任务读取的 key-value对,输入给 Map 任务。

(3) Map 任务会根据用户自定义的映射规则,输出一系列的作为中间结果。

(4) 为了让 Reduce 可以并行处理 Map 的结果,需要对 Map 的输出进行一定的分区(Partition)、排序(Sort)、合并(Combine)、归并(Merge)等操作,得到形式的中间结果,再交给对应的 Reduce 进行处理,这个过程称为 Shuffle。从无序的到有序的,这个过程用 Shuffle(洗牌)来称呼是非常形象的。

(5) Reduce 以一系列中间结果作为输入,执行用户定义的逻辑,输出结果给OutputFormat 模块。

(6) OutputFormat 模块会验证输出目录是否已经存在以及输出结果类型是否符合配置文件中的配置类型,如果都满足,就输出 Reduce 的结果到分布式文件系统


Shuffle 过程详解

所谓 Shuffle,是指对 Map 输出结果进行分区、排序、合并等处理并交给 Reduce 的过程。因此,Shuffle过程分为 Map 端的操作和 Reduce 端的操作


Map端的Shuffle

    Map 的输出结果首先被写入缓存,当缓存满时,就启动溢写操作,把缓存中的数据写入磁盘文件,并清空缓存。当启动溢写操作时,首先需要把缓存中的数据进行分区,然后对每个分区的数据进行排序(Sort)和合并(Combine,如果指定的话),之后再写入磁盘文件。每次溢写操作会生成一个新的磁盘文件,随着 Map 任务的执行,在磁盘中就会生成多个溢写文件。在 Map 任务全部结束之前,这些溢写文件会被归并(Merge)成一个大的磁盘文件,然后通知相应的 Reduce 任务来领取属于自己处理的数据。

1)输入数据和执行 Map 任务

2)写入缓存

    每个 Map 任务都会被分配一个缓存(环形缓冲区,默认100MB,溢写比例是0.8),Map 的输出结果不是立即写入磁盘,而是首先写入缓存。在缓存中积累一定数量的 Map 输出结果以后,再一次性批量写入磁盘,这样要以大大减少对磁盘 I/O 的影响(寻址开销)。需要注意的是,在写入缓存之前,key 与 value 值都会被序列化成字节数组。

3)分区

缓存中的数据首先会被分区(Partition),MapReduce 通过 Partitioner 接口对这些键值对进行分区,默认采用的分区方式是采用 Hash 函数对 key 进行哈希后再用 Reduce 任务的数量进行取模,可以表示成 hash(key) mod R

4)排序

    对于每个分区内的所有键值对,根据 key 对它们进行内存排序(Sort),排序是 MapReduce的默认操作。

5)合并Combiner

    排序结束后,如果用户事先定义了 Combiner 函数,则这个时候会执行合并操作,从而减少需要溢定到磁盘的数据量。如果用户事先没有定义 Combiner函数,就不会进行合并操作。

    所谓“合并(Combiner)”,是指将那些具有相同 key 的的 value 加起来。比如,<’aaa’,1>和<’aaa’,1>     ---->       <’aaa’,2>,从而减少了键值对的数量。

    并非所有场合都可以使用 Combiner,因为 Combiner 的输出是 Reduce 任务的输入,Combiner 绝不能改变 Reduce 任务最终的计算结果,一般而言,累加、最大值等场景可以使用合并操作。

    经过分区、排序以及可能发生的合并操作之后,这些缓存中的键值对就可以被写入磁盘,并清空缓存。

6)溢写Spill

    提供给 MapReduce 的缓存的容量是有限的,默认大小是 100MB,当 100MB 大小的缓存被填满 80MB 数据时,就启动溢写过程,把已经写入的 80MB 数据写入容一次性写入磁盘,并清空缓存,剩余 20MB 空间供 Map 结果继续写入(为了保证 Map 结果能够不停地持续写入缓存,不受溢写过程的影响)

    每次溢写操作都会在磁盘中生成一个新的溢写文件,写入溢写文件中的所有键值对都是经过分区和排序的。

7)归并Merge

    在 Map 任务全部结束之前,会对所有溢写文件中的数据进行归并(Merge),生成一个大的溢写文件,这个大的溢写文件中的所有键值对也是经过分区和排序的

    所谓“归并”,是指对于具有相同 key 的键值对会被归并成一个新的键值对。具体而言,对于若干个具有相同 key 的键值对<k1,v1>,<k2,v2>,...,<kn,vn>会被归并成一个新的键值对<k1,<v1,v2,...,vn>>。

    进行文件归并时,如果磁盘中已经生成的溢写文件的数量超过参数 min.num.spills.for.combine的值时(默认值是 3,用户可以修改这个值),那么,就可以再次运行 Combiner,对数据进行合并操作,从而减少写入磁盘的数据量(进而也减少了网络传输量)。

    经过上述 步骤以后,Map 端的 Shuffle 过程全部完成,最终生成的一个大文件会被存放在本地磁盘上。这个大文件中的数据是被分区的,不同的分区会被发送到不同的 Reduce 任务进行并行处理。MRApplicationMaster 会一直监测 Map 任务的执行,当监测到一个 Map 任务完成后,就会立即通知相关的 Reduce 任务来“领取(Fetch)”数据,然后开始 Reduce 端的 Shuffle 过程。

Reduce端的Shuffle


(1)“领取(Fetch)”数据

    Map 端的 Shuffle 过程结束后,所有 Map 输出结果都保存在 Map 机器的本地磁盘上,Reduce 任务需要把这些数据“领取”回来存放到自己所在机器的本地磁盘上。因此,在每个 Reduce 任务真正开始之前,它大部分时间都在从 Map 端把属于自己处理的那些分区的数据“领取”过来。每个 Reduce 任务会不断地通过 RPC 向 MRApplicationMaster 询问 Map 任务是否已经完成;MRApplicationMaster 监测到一个 Map 任务完成后,就会通知相关的 Reduce 任务来“领取”数据;一旦一个 Reduce 任务收到 AM的通知,它就会到该 Map 任务所在机器上把属于自己处理的分区数据拷贝到本地磁盘中。一般系统中会存在多个 Map 机器,因此 Reduce 任务会使用多个线程同时从多个 Map 机器领回数据。

(2)归并数据

    从 Map 端领回的数据会首先被存放在 Reduce 任务所在机器的缓存中,如果缓存被占满,就会像Map 端一样被溢写到磁盘中。由于在 Shuffle 阶段 Reduce 任务还没有真正开始执行,因此,这时可以把内存的大部分空间分配给 Shuffle 过程作为缓存。需要注意的是,系统中一般存在多个 Map 机器,Reduce任务会从多个 Map 机器领回属于自己处理的那些分区的数据,因此缓存中的数据是来自不同的 Map 机器的,一般会存在很多可以合并(Combiner)的键值对。

    当溢写过程启动时,具有相同 key 的键值对会被归并(Merge),如果用户定义了 Combiner,则归并后的数据还可以执行合并操作,减少写入磁盘的数据量。每个溢写过程结束后,都会在磁盘中生成一个溢写文件,因此磁盘上会存在多个溢写文件。

    最终,当所有的 Map 端数据都已经被领回时,和 Map 端类似,多个溢写文件会被归并成一个大文件,归并的时候还会对键值对进行排序,从而使得最终大文件中的键值对都是有序的。

    在数据很少的情形下,缓存可以存储所有数据,就不需要把数据溢写到磁盘,而是直接在内存中执行归并操作,然后直接输出给 Reduce 任务

(3)把数据输入给 Reduce 任务

    将若干个大文件输入给Reduce任务,Reduce task 会执行 Reduce 函数中定义的各种映射,输出最终结果,并保存到分布式文件系统中(比如 GFS 或 HDFS)








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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值