MapReduce的原理

 

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

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

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

      MapReduce框架由一个单独的master JobTracker和每个集群节点一个slave TaskTracker共同组成。master负责调度构成一个作业的所有任务,这些任务分布在不同的slave上,master监控它们的执行,重新执行已经失败的任务。而slave仅负责执行由master指派的任务。

       应用程序至少应该指明输入输出的位置(路径),并通过实现合适的接口或抽象类提供map和reduce函数。再加上其他作业的参数,就构成了作业配置(job configuration)。然后,Hadoop的jobclient提交作业(jar包可执行程序等)和配置信息给JobTracker,后者负责分发这些软件和配置信息给slave、调度任务并监控它们的执行,同时提供状态和诊断信息给job-client。

    输入与输出:MapReduce框架运转在<key,value>键值对上,也就是说,框架把作业的输入看为是一组<key,value>键值对,同样也产出一组<key,value>键值对做为作业的输出,这两组键值对的类型可能不同。

      框架需要对key和value的类(classes)进行序列化操作,因此,这些类需要实现Writable接口。另外,为了方便框架执行排序操作,key类必须实现WritableComparable接口。一个MapReduce作业的输入和输出类型如下所示:

(input)<k1,v1>->map-><k2,v2>->combine-><k2,v2>->reduce-><k3,v3>(output)

Hadoop中的MapReduce工作流程简介

一般来讲,Hadoop的一个简单的MapReduce任务执行流程如图5.2,5.3所示

标题

 

 

标题

1).JobTracker负责分布式环境中实现客户端创建任务并提交。

2).InputFormat模块负责做Map前的预处理,主要包括以下几个工作:验证输入的格式是否符合JobConfig的输入定义,可以是专门定义或者是Writable的子类。将input的文件切分为逻辑上的输入InputSplit,因为在分布式文件系统中blocksize是有大小限制的,因此大文件会被划分为多个较小的block。通过RecordReader来处理经过文件切分为Inputsplit的一组records,输出给Map。因为Inputsplit是逻辑切分的第一步,如何根据文件中的信息来具体切分还需要RecordReader完成。

3).将RecordReader处理后的结果作为Map的输入,然后Map执行定义的Map逻辑,输出处理后的(key,value)对到临时中间文件。

4).Combiner是可选择的,它的主要作用是在每一个Map执行完分析以后,在本地优先作Reduce的工作,减少在Reduce过程中的数据传输量。

5).Partitioner也是选择配置,主要作用是在多个Reduce的情况下,指定Map的结果由某一个Reduce处理,每一个Reduce都会有单独的输出文件。

6).Reduce执行具体的业务逻辑,即用户编写的处理数据得到结果的业务,并且将处理结果输出给OutputFormat。

7).OutputFormat的作用是,验证输出目录是否已经存在和输出结果类型是否复合Config中配置类型,如果都成立,则输出Reduce汇总后的结果。

Hadoop中MapReduce的任务调度

首先要保证master节点的NameNode,SecondedNameNode,JobTracker和slaves节点的DataNode,TaskTracker都已经启动。通常MapRedcue作业是通过JobClient.rubJob(job)方法向master节点的JobTracker提交的,JobTracker接到JobClient的请求后把其加入作业队列中。JobTracker一直在等待JobClient通过RPC向其提交作业,而TaskTracker一直通过RPC向JobTracker发送心跳信号询问有没有任务可做,如果有,则请求JobTracker派发任务给它执行。如果JobTracker的作业队列不为空,则TaskTracker发送的心跳将会获得JobTracker给它派发的任务。这是一个主动请求的任务:slave的TaskTracker主动向master的JobTracker请求任务。当TaskTracker接到任务后,通过自身调度在本slave建立起Task,执行任务。图5.4是MapReduce任务请求调度的过程示意图:

标题

 

具体来说,这个过程包括两个步骤:

1).JobClient提交作业

JobClient.runJob(job)静态方法会实例化一个JobClient的实例,然后用此实例的submitJob(job)方法向JobTracker提交作业。此方法会返回一个RunningJob对象,它用来跟踪作业的状态。作业提交完毕后,JobClient会根据此对象开始关注作业的进度,直到作业完成。submitJob(job)内部是通过调用submitJobInternal(job)方法完成实质性的作业提交的。submitJobInternal(job)方法首先会向hadoop分布系统文件系统(HDFS)依次上传三个文件:job.jar,job.split和job.xml。job.jar里面包含了执行此任务需要的各种类,比如Mapper,Reducer等实现;job.split是文件分块的相关信息,比如有数据分多少个块,块的大小(默认64M)等。job.xml是有关的作业配置,例如Mapper,Combiner,Reducer的类型,输入输出格式的类型等。

2).JobTacker调度作业

JobTracker接到JobClient提交的作业后,即在JobTracker.submitJob(job)方法中,首先产生一个JobInProgress对象。此对象代表一道作业,它的作用是维护这道作业的所有信息,包括作业相关信息JobProfile和最近作业状态JobStatus,并将作业所有规划的Task登记到任务列表中。随后JobTracker将此JobInProgress对象通过listener.jobAdded(job)方法加入到调度队列中,并用一个成员变量jobs来维护所有的作业。然后等到有TaskTracker空闲,使用JobTracker.AssignTask(tasktracker)来请求任务,如果调度队列不空,程序便通过调度算法取出一个task交给来请求的TaskTracker去执行。至此,整个任务分配过程基本完成,各个类的相互关系和依赖性如图5.5所示:

 

标题

 

       现在,Hadoop自带的调度策略规定是先进先出(FIFO)的,很多系统也是直接用它。虽然FIFO策略简单稳定,但随着用户和服务的日益增多,特别是服务等级的区分日益明显,高资费的用户希望拥有更优先的服务,因此FIFO没有办法适应越来越多的Hadoop商业应用需求。相关的开发种也有人考虑队列容量分配和公平队列算法,但算法实现都不够实用,也没有认真分析Hadoop中服务优先区分的具体要求。因此,本文将重新考虑其调度策略,在考虑服务等级区分和尽量保证公平的想法基础上,提出新的调度算法,并编码实现。

基于优先级的加权轮转算法

调度算法相关研究

       由于Hadoop的MapReduce调度是由tasktracker主动向jobtracker请求的,其原理类似于普通的非抢占式操作系统调度,即任务一旦分配,就不可中断。根据调研,已有典型调度算法如下:

1).先进先出算法(FIFO:Fisrt In First Out):该算法按照进程进入就绪队列的先后顺序来选择。即每当进入进程调度,总是把就绪队列的队首进程投入运行。Hadoop自带的调度算法就是FIFO。

2).时间片轮转算法(RR:Round-Robin):主要是分时系统中使用的一种调度算法。轮转发的基本思想是:将cpu划分成一个个时间片,就绪队列中的就绪进程轮流运行一个时间片。在Hadoop,可以将每个task请求看作一个时间片,用轮转的方法来调度可以使得所有job的task轮流被调度。

3).最高优先级算法(HPF:Height Priority First):该算法调度进程时,每次将处理即分配给具有最高优先级的就绪进程。进行优先数的设置可以时静态的,也可以是动态的。静态优先数是在进程创建是根据进程初始特性或用户要求而确定的,在进程运行期间不能再改变。动态优先数则是指再进程创建时先确定一个初始优先数,以后可以在进程运行中随着进程的特性改变。

4).加权轮循算法(WRR:Weighted Round Robin):在网络路由领域,不少研究考虑了加权的轮循算法,本文将该算法思想引入Hadoop的任务调度中,结合Hadoop任务处理的特性,提出了适合于该平台的特有的基于优先级的加权轮转算法。

基于优先级的加权轮转算法

考虑到TaskTracker主动请求task的模式和hadoop任务调度体系非抢占式的特点;为了使调度的任务避免长期的等待,同时各个任务调度优先级又能够根据实际情况调整,我们将基于加权轮转调度算法的基本思想,综合考虑hadoop网络调度需求的实际情况,提出适合Hadoop任务调度的基于优先级的加权轮转调度算法(Priority Based Weighted Round Robin,PBWRR)。

算法基本思想为:将各个待运行的job放入一个队列,在不加权的情况下,轮流将任务的task交给tasktracker去执行。加权的情况下,权重较大的job可以在一次轮流中执行多个tasks。

算法的基本步骤是:

1).在tasktracker资源可用的情况下,tasktracker主动向jobtracker提交任务分配请求。

2).Jobtraker接受到tasktraker的任务分配请求后,考虑调度队列中当前job的一个task交给请求的tasktracker去执行,更新该job的剩余task信息,同时将该job的thisRoundTask值减1,如果减1后thisRoundTask结果小于1,将指针移动到下一个job;否则指针不动,等待下一个tasktracker请求的到来。

3).当指针到达队尾时,更新整个队列的相关信息,如果有job完成工作或者新的job加入,则重新计算每个job的thisRoundTask值,并将指针移动到队列的开始。

整个算法的流程示意如图5.6

 

标题

 

jobQueue的调度队列中每个元素jobInfo的数据结构为:

Class jobInfo

{

Int JobId;

Int jobSize;

Int taskNum;

Int meanTaskSize;

Int priority;

Int weight;

Int ThisRountTask;

}

整个调度中涉及两种队列,jobQueue和taskQueue[].其中jobQueue的元素为jobInfo,每个taskQueue中的所有元素对应一个job的所有map或reduce的task。在本文编程实现中,所有queue均使用数组实现。

分配任务的方法assignTasks具体算法下:

synchronized List<Task>assignTasks(TaskTrackerStatustracker)

{

1).首先考察整个jobQueue中是否有等待处理的map task或reduce task任务,如果没有返回null。

2).考察当前jobQueue中指针指向的jobInfo,根据该jobInfo的JobId找到对应的taskQueue[i]。

3).考察其为maptask Queue还是Reducetask Queue,如果是Reducetask Queue,需要考虑其对应的maptask是否已完成,如果其对应的maptask没有完成,需要将jobQueue的指针后移,重新考虑步骤2。如果是maptask Queue或者是Reducetask Queue且其对应的maptask已经完成,则分配该Queue的队首task给请求任务的tasktracker。

4).从分配task的taskQueue中删除该task并更新该taskQueue的统计信息,更新jobQueue中对应的jobInfo信息(包括thisRoundTask-1,doneTask+1等)。如果更新后的jobInfo的thisRoundTask值小于1,则将jobQueue指针后移,否则指针保持不动。

5).如果指针已经移动到队尾,则根据jobQueue更新后的jobInfo信息,如果有job完成任务或者有新的job加入,则重新计算所有的jobweight,即每个job的thisRoundTask值,再将指针移动至队首。

6).返回步骤3所分配的task。

}

其中步骤5中的权重及其thisRoundTask计算方法如下:

updateJobQueue(jobQueue queue1)

{

weight=calculateWeight(priority);

thisRoundTask=Min(floor(weight),taskNum);

}

其中priority的取值在运行时配置,由于云计算提供的业务种类繁多,收费和服务质量要求也各不相同,因此priority的取值也比较丰富。但在新增业务及其priority时,必须参考以前所有业务的priority取值,从而保持整个系统的相对公平性。当然,这应该时在业务规划时考虑的问题,而不是本文思考的重点。在本文的实验中,我们就简单的将priority取值为1、2、3等整数值。thisRoundTask为weight下取整和该job剩余的taskNum中取较小的值。

权重计算方法推导过程:

每个job[i]的meanTaskSize[i]=jobSize[i]taskNum[i];如果job[i]的权重为weight[i],则整个系统一个调度周期处理的数据量为:

同时,如果每个job[i]有优先级priority[i],为了保证其优先级的有效性,我们应该使得在这个周期中该job的数据处理量占整个系统数据处理量的百分比基本等于该job优先级值占整个系统优先级值之和的百分比,即:

 

其中priority[i]和meanTaskSize[i]都是已知的,我们唯一需要考虑的就是整个系统一个周期处理的数据量S。

考虑系统的处理能力,由于系统应用的绝大多数task的size都应该为blockSize,因此我们考虑整个系统所支持的MapTask能力(因为一般情况下ReduceTask需求小于mapTask)taskAbility,因此S可以定义为公式5.4

其中num表示一个周期内平均每个job执行的task数量,jobNum为待处理

的job个数,在本文实验中,我们对num取值2。

假设整个系统有m个pc组成,第i个pc可以支持t[i]个tasktracker,每个tasktracker的单位处理能力为pt[i],整个系统的单位处理能力就为:

其中p[i]为这台pc分配给Hadoop的整个处理能力,因为在单核计算机中,CPU是进程时分复用的,因此每个tasktracker的处理能力和每个单位tasktracker的处理能力的乘积就等于pc分配给Hadoop的整个处理能力p[i]。我们通过P值和S值可以估计系统一个周期运行的大致时间,但由于调度和实际任务的size问题,实际运行时间和估计时间由一定差距。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值