MapReduce执行过程

当我们需要把数据存储在分布式文件系统HDFS,由MapReduce计算移动到存储有部分数据的各台机器上,下面我们看看具体过程。

首先上一点干货:

MapReduce作业(job)是客户端需要执行的一个工作单元:它包括输入数据,MapReduce程序和配置信息。Hadoop将作业分布称若干小任务(task)来执行,其中包括两类任务:map任务(计算)和reduce(聚合)任务;

有两类节点控制着作业执行过程:一个JobTracker和n个TaskTracker。JobTracker通过调度TaskTracker上运行的任务,来协调所有运行在系统上的作业。TaskTracker在运行任务的同时将运行的进度报告发送给JobTracker,JobTracker通过这种方式记录每一项作业任务的整体进度情况。如果其中一个任务失败了,JobTracker可以在另一个TaskTracker节点上重新调度该任务。

Hadoop将MapReduce的输入数据划分成等长的小数据块,称为输入分片(input split)简称分片。Hadoop为每一分片构建一个map任务,并由该任务来运行用户自定义的map函数,来计算处理分片中的每一天记录。

拥有许多分片,意味着处理每个分片所需要的时间少于处理整个输入数据所花的时间。因此,如果我们并行处理每个分片,且每个分片数据比较小,那么整个处理过程将获得更好的负载均衡。

因为一台较快的计算机能处理的数据分片比一台较慢的计算机更多,且成一定的比例。即使使用相同的机器,处理失败的作业或其他同时运行的作业也能够实现负载平衡,并且如果分片被切分得更细,负载平衡的质量会更好。

clip_image002

但是,如果分片切分得太小,那么管理分片的总时间和构建map任务的总时间将决定着作业的整个执行时间。对于大多数作业来说,一个合理的分片大小趋向于HDFS的一个块的大小的倍数,默认是64MB,我们可以通过业务数据的大小来调整这个值。

Hadoop在存储有输入数据(HDFS中的数据)的节点上运行map任务,可以获得最佳性能。这就是所谓的数据本地化优化(data locality optimization)。

我们之所以让分片和块的大小相等,是因为它确保可以存储在单个节点上的最大输入块的大小。如果分片跨越两个数据块,那么对于任何一个HDFS节点,基本不可能同时存储两个数据块,因此分片中的部分数据需要通过网络传输到map任务节点。与使用本地数据运行整个map任务相比,这种方法显然效率更低。

map任务将其输出写入本地硬盘,而非HDFS。是因为map的输出是中间结果,经过reduce任务处理后就没有意义了。如果该节点上运行的map任务在将map中间结果传送给reduce任务之前失败,hadoop将在另一个节点上重新运行这个map任务以再次构建map中间结果。

Reduce任务并不具有数据本地化的优势—单个reducer任务的输入通常来自于所有mapper的输出。因此,排过序的map输出需通过网络传输发送到运行reduce任务的节点。

数据在reduce端合并,然后由用户定义的reduce函数处理。reduce的输出通常存储在HDFS中以实现可靠存储。对于每个reduce输出的HDFS块,第一个副本存储在本地节点上,其他副本存储在其他机架节点中。因此,reduce的输出写入HDFS会占用网络带宽,这与正常的HDFS流水线写入的消耗一样。

clip_image003

一个reduce任务的MapReduce数据流

虚线框表示节点,虚线箭头表示节点内部的数据传输,而实线箭头表示节点之间的数据传输。

reduce任务的数量并非由输入数据的大小决定,而是特别指定的。如有多个reduce任务,则每个map任务都会对其输出进行分区(partition),即为每个reduce任务建一个分区。每个分区有许多键(及其对应值),但每个键对应的键/值对记录都在同一分区中。分区由用户定义的分区函数控制,但通常默认的分区器(partitioner,文中有时也称“分区函数”)通过哈希函数来分区,这种方法很高效。

一般情况下,多个reduce任务的数据流如下图所示。该图清楚地表明了为什么map任务和reduce任务之间的数据流称为shuffle(混洗),因为每个reduce任务的输入都来自许多map任务。混洗比此图更复杂,并且调整混洗参数对作业总执行时间会有非常大的影响。

clip_image004

多个reduce任务的数据流

最后,也有可能没有任何reduce任务。当数据处理可以完全并行时,即无需混洗,可能会出现无reduce任务的情况。在这种情况下,唯一的非本地节点数据传输是map任务将结果写入HDFS。

clip_image005

无reduce任务的MapReduce数据流

Combiner

集群上的可用带宽限制了MapReduce作业的数量,因此最重要的是一点是尽量避免map任务和reduce任务之间的数据传输。Hadoop允许用户针对map任务的输出指定一个合并函数combiner,就像mapper和reducer一样(合并函数的规则限定了可以使用的函数类型)。合并函数的输出作为reduce函数的输入。由于合并函数是一个优化方案,所以Hadoop无法确定针对map任务输出中任意一条记录需要调用多少次合并函数。也就是说,无论调用合并函数多少次,reducer的输出结果都应一致。

注意:Combiner是每个mapper后聚合一次,reducer是所有mapper后聚合,所以在求和,求最大等时,结果是一致的。

但是求平均数等的时候就会出现结果不一致。

指定一个合并函数:

在Java MapReduce程序,合并函数是通过Reducer接口来定义的,并且该列中,它的实现与reduce函数相同。唯一需要做的修改是在Job中设置combiner类( job.setCombinerClass(MyReducer.class);)。

posted @ 2013-08-16 22:32  潜伏的蛟龙  阅读(1093)  评论(0编辑  收藏  举报