第二章 第四节 扩大规模

    我们已经看到MapReduce是如何在小规模输入上工作;现在是时候从总体上

看一下这个系统以及大规模输入下的数据流。简单起见,目前为止使用的例子

都是使用本地的文件系统。尽管如此,为了扩大规模,我们需要把数据存储在

分布式文件系统中(典型的如HDFS,下一章中我们会学习它)。它允许HADOOP

把MapReduce计算移到到每一个保存了部分数据的机器上,使用HADOOP资源

管理系统,名叫YARN(见第四章)。让我们看它是如何工作的。


数据流

    首先,一些术语。MapReduce job是一个客户端上需要被执行的工作单元:它由

输入数据,MapReduce程序,以及配置信息组成。HADOOP把它分成tasks来运行

job,task有两种类型:map tasks 和 reduce tasks。tasks由YARN调度,运行在

集群上的节点上。如果一个task失败,它会自动重新调度运行在一个不同的节点上。

    HADOOP把输入分成固定大小的块叫做 input split或是just split,来交由MapReduce job

来处理。所以如果我们并行处理split,当splits小的时候这些处理最好是负载均衡的,因为

运行快的机器比运行慢的机器在job运行期间处理的split要更多。即使机器都是一样的,

处理失败或是job同步运行都需要负载均衡,而且如果split划分的更好,负载均衡的

效果会更好。

    另一方面,如果split太小,管理splits和创建map tast的开销会占用大部分的job处理时间。

对于大多数job来说,一个合适的split的大小往往是HDFS block的大小,默认为128M,尽管

它可以被改变(新建的文件)或是文件创建时指定。

    HADOOP尽量在存储了输入数据的节点上运行map task,因为它不会占用宝贵的集群带宽。

这叫做数据本地优化。尽管如此,有时,所有保存map task对应的input split的HDFS块复制品

的节点都在运行别的map task,所以job调度者会寻找一个对应数据块所在的节点同一架上的

空闲节点。万一这个也找不到,则使用离架节点,它将导致架间网络传输。这三种情况如图2-2所示:


    现在应该清楚为什么优化的split大小和block的大小一致了:它是可以确保保存在同一个节点的输入的最大值。如果

split被拆分成两个block,所有的HDFS节点都保存这两个block似乎不太可能,所以有些split需要通过网络在节点间

传输,这样明显比使用本地数据来运行整个map task要低效。

    map task把它们的输出写到本地,而不是HDFS。为什么这样?map输出是一个中间输出:它被reduce task处理

来产生最终输出,并且一旦job完成,map输出就可以被扔了。所以,把它保存在HDFS并复制是小题大做的。如果

这个运行map task的节点在map输出被reduce task使用之前失败了,那么HADOOP会自动在另一个节点上运行map task

来重新生成map输出。

    reduce task没有数据本地化的优势;reduce task的输入往往是所有map的输出。当前的例子中,我们只有一个reduce task,

所有的map task输出都交给它。因此,排序后的map输出需要通过网格传输到reduce task工作的节点上,在那里

它们将被合并然后传给用户定义的reduce函数。reduce的输出通过保存在HDFS中。因此,写出reduce输出是肯定

会消耗网络带宽的,但是也仅仅和普通的HDFS写操作消耗一样。

    单个reduce task的数据流如图2-3所示。点组的框代表节点,节箭头显示数据在节点中传输,实线箭头表示数据在

节点间传输。

    reduce task的数目不是受输入的大小的支配,它们各自单独定义。在214页,你将看到如果选择reduce task数目。

    当有多个reduce时,map task 拆分它们的输出, 为每一个reduce task创建一个分块。每一分块可以有许多键(以及

与之相关的值),但是对于给定的键,对应的记录都在同一分块里。这个拆分可以由用户定义的拆分函数来控制,

但通常使用默认的拆分器---使用哈希函数分键----工作的很好。

    多reduce task一般情况的数据流如图2-4所示。这个图表可以清楚的看到为什么map和reduce之间的数据流被

通俗的称为“洗牌”,每一个reduce task由多个map task供养。“洗牌”比图表显示的要更复杂,调节它对job的执行

时间有重大影响,你可以在197页“洗牌和排序”中看到它。

    最后,有零个reduce task也是可能的。当所有处理都可以并行时你不需要“洗牌”,这个时候也就不需要reduce task(

234页会讲到一些例子)。在这种情况下,唯一的跨节点数据传输是map task写出到HDFS(见图2-5)


组合器函数

    许多MapReduce job被集群的带宽限制,所以最小化在map和reduce task之间数据传输是值得做的。HADOOP允许

用户自定义组合函数来处理map输出,组合器的输出形成reduce函数的输入。由于组合器函数是一个优化,HADOOP

不保证对一个特定的map输出调用多少次。换句话说,调用组合器函数零次,一次,或多次,reduce应该产生同样的输出。

    组合器函数的契约限制了它可以使用的函数类型。这最好通过例子讲解。想想那个最高温度的例子,1950年的计数由

两个map处理(因为他们在不同的split)。设想第一个map产生如下输出:

(1950, 0)
(1950, 20)
(1950, 10)
第二个输出:
(1950, 25)
(1950, 15)
reduce函数被调用,并提供所有的值:

(1950, [0, 20, 10, 25, 15])
产生输出:

(1950, 25)
    因为25是列表中的最大值。我们可以使用一个组合器函数,就像reduce函数一样,找到每一个map输出的最高温度。

reduce函数会被调用,提供如下的值:

(1950, [20, 25])
并且会产生与之前一样的输出。更简洁的来看,我们可以用如下公式表达函数的调用:
max(0, 20, 10, 25, 15) = max(max(0, 20, 10), max(25, 15)) = max(20, 25) = 25
并不是所有的函数都持有这个属性。例如,如果我们计算平均温度,我们不可以平均来做为我们的组合器函数,因为:

mean(0, 20, 10, 25, 15) = 14
但:
mean(mean(0, 20, 10), mean(25, 15)) = mean(10, 20) = 15
组合器函数不会代替reduce函数。(怎么可能?reduce函数还需要处理不同map产生的相同键的记录)。但是它有助于

减少在map和reduce之间需要“洗牌”的数据总量,仅仅基于这个原因通常也值得你考虑 是否需要使用一个组合器函数在

你的MapReduce job中。


定义一个组合器函数

    回到 Java MapReduce程序,组合器函数使用Reducer类定义,对于这个应用来说,它和reduce函数的实现一样。唯一

改变的是我们需要把组合器函数设置到Job(见例2-6)

public class MaxTemperature {
    public static void main(String[] args) throws Exception {
        if (args.length != 2) {
            System.err.println("Usage: MaxTemperature <input path> <output path>");
            System.exit(-1);
        }
        Job job = new Job();
        job.setJarByClass(MaxTemperature.class);
        job.setJobName("Max temperature");
        FileInputFormat.addInputPath(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new Path(args[1]));
        job.setMapperClass(MaxTemperatureMapper.class);
        job.setReducerClass(MaxTemperatureReducer.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);
        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }
    
}


运行分布式MapReduce Job

    同样的程序,不需要修改,在一个全部的数据集上。这就是MapReduce的重点:它与你的数据及硬件大小按比例

增减。这里有一个数据:在一个10个节点(High-CPU Extra Large)的EC2集群上,这个程序运行了6分钟。

    我们将在第六章详细讲解集群程序运行的细节。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值