MapReduce是一个面向大数据并进行处理的计算模型和框架。主要采用将海量数据分发到计算机集群进行运算,最后将结果汇总的先拆分再合并的思想。这样做最大好处就是成本低,速度快,计算机集群采用的都是普通的配置,联合集群中单个节点的运算能力,提高整体的处理速度,并且可以增加节点的数量来提高计算性能。
在处理数据时,通过将Map调用的输入数据,自动分割为M个数据片段的集合,将这些数据片段分发到不同的节点上并行处理,而进行分发,汇总等等资源调度的是一个叫master的进程。master是这个MapReduce执行过程中的大脑,有且只有一个。master存储着每一个Map和Reduce任务的状态,以及worker的标识(非空闲)。master进程首先分配任务,将Map任务或Reduce任务分发给空闲的worker。在处理Map任务的worker处理完成后,将结果存储在本地磁盘,而将存储位置、大小返还给master。master再将这个位置信息分配给Reduce worker。当Map和Reduce都处理完成后,master再把全部的结果返还给用户。master 还会定期ping每一个worker,如果再约定时间没有回应,就认为此worker故障失效,这个故障worker中正在处理以及已经处理完成的Map任务都将被重新设定为初始的状态,等待重新分配,这极大的提高了处理过程中的容错率。如果是Reduce的worker故障,但任务已经完成,则不需要重新初始化,因为Reduce的任务输出存储在全局文件系统上。既然是大脑,核心程序,为防止master失效,需要定期的将已存储的信息写入磁盘。然而现实是,由于只有一个master,所以失效,就要重新启动程序。
Map中的M个数据片段是由用户在进行数据切片时决定的,而Map任务产生R个中间文件中的R也是根据用户需求决定的,在每个Map机器上,最终产生R个中间文件,所有中间文件的个数为M*R。
Shuffle阶段描述着数据从map task输出到reduce task输入的这段过程。当Map执行完成时将结果解析为key/value pair的形式存储在本地磁盘上,分区函数(Partitioner)决定将Map处理结果分配给哪个相应的reduce,然后将数据读入缓冲区,缓冲区只设了100M,如果内存达到80M就会发生spill(溢写)到磁盘中。在磁盘中,如果用户设定了combiner函数(继承Reducer类,相当于提前进行一次Reduce阶段的合并),就会在此时进行数据的合并。然后所有的数据文件就到放在了磁盘中,因为最终磁盘中只有一个溢写文件,所有此时会进行merge(归并)将数据化为group,这个归并是将本机中map处理不同的溢写文件归并,所有key也可能相同,此时也会调用combiner再次进行合并,这样减少在Reduce阶段的时间,进而提高整体运行时间。
同时,它还会通过备用(backup)任务进程,在程序接近完成时,帮助那些因为各种原因导致处理慢的任务,减少运算过程中“落伍者”的出现,这样可以减少在进行大规模数据处理时的总处理时间。
一些用户数据之中存在一些bug会使的worker在处理Map和Reduce任务时会直接crash掉。而为了应对这种情况的发生每个worker进程都设置了信号处理函数捕获内存段异常(segmentation violation)和总线错误(bus error)。在执行 Map 或者 Reduce 操作之前,MapReduce 库通过全局变量保存记录序号。如果用户程序触发了一个系统信号,那么这个相应的Reduce就将被直接跳过不再进行处理。
最后关于存储位置的设定,在我们的计算运行环境中,应该尽量把输入数据存储在集群中机器的本地磁盘上来节省网络带宽,文件是按block分隔划分的,每个block保存在多个机器上,倘若机器故障不会导致数据丢失,增强了数据的可靠性。master在调度Map任务时会考虑文件的的存储位置,尽量在存储数据的本地机器上进行作业,或者在附近的机器上执行,就近原则。在足够大的集群上运行大型的MapReduce操作时,大部分输入数据都是在本地机器上读取的。