第二章Mapreduce

数据流(统一:job 译为作业,task译为任务)

首先,说些术语。一个mapreduce作业是客户端要执行的一个工作单元,它包括:输入数据,mapreduce程序,和配置信息。Hadoop把作业分割成任务来运行,分割成的任务分两种,map任务,reduce任务。

有两种类型的节点来控制作业的执行:一个作业tracker和很多的任务tracker。作业tracker通过规划运行在任务tracker上的任务,来协调所有的任务。任务tracker执行任务,并且把进度报告给作业tracker,这样,作业tracker记录了每一个任务的进度。如果一个任务失败了,作业tracker可以重新把他规划到一个不用的任务tracker上面。

Hadoopmapreduce作业的输入数据分割成固定的小块,叫做输入小块。Hadoop为每一个小块建一个map任务,这个map任务对小块中的每一行记录,执行用户指定的map函数。

有很多的小块,意味着处理每一个小块的时间,与处理整个输入的时间相比,是很短的。因此,如果我们并行的处理这些小块,那么,这个处理过程最好是负载平衡的。原因是:性能好的机器会比性能差点的机器处理更多的小块。即便机器的性能是一样的,失败的处理,或者其他的任务都在同时进行,都能够使负载平衡令人满意,但,当小块变得更小时,负载平衡的问题就会增加。

另一方面,如果小块太小的话,那么管理小块和创建map任务的时间就会占据作业执行时间很大一部分了。对于大多数作业来说,一个合适的大小是HDFS块的大小,默认是64M,尽管对于集群来说,这个值是可以改变的(对所有新创建的文件),或者当文件创建的时候指定大小。

Hadoop尽量在输入数据在HDFS中的节点上执行map任务。这叫做数据本地化优化。那么现在,为什么小块的合适大小和块大小一样的这个问题就很清楚了。这个大小是可以被保证存储在一个单独节点上的数据的最大值。如果有的小块横跨了两个块,任何一个HDFS节点存储两个块是不可能的,那么,这些小块不得不通过网络运输到执行map任务的节点上,这样以来的话,很显然,这比整个map任务用它的本地(本节点上的)数据处理效率要低。

Map任务把他们的输出写到本地,并不是HDFS上面,因为,map的输出时中间产品,他是被reduce任务处理来输出最终输出结果的,并且一旦任务完成,map输出内容就可以被抛弃了。因此,在HDFS中对他们进行排序,并复制一份,这是浪费。如果,执行map任务的节点在输出数据被reduce处理前失败了,那么,hadoop会自动的把这个map任务重新放到两一个节点上去重新执行map任务,得到map任务输出结果。

Reduce任务没有数据本地化的优势,一个reduce任务的输入一般情况下是所有mappers的输出。因此,排序好的map输出数据需要通过网络,传输到reduce任务执行的节点上面,在节点上面,这些数据整合好,输入给用户指定的reduce函数。Reduce的输出数据,可靠起见,通常存储在HDFS上面。第三章中会讲到,对于每一个reduce 输出数据HDFS块,第一个备份在本机节点上面。另一个备份在其它的机架上面。这样的话,写入reduce输出数据会消耗网络带宽,但是这种消耗仅仅和一个普通的HDFS写入是一样的。

只有一个reduce任务的整个数据流图表如下:小方框代表节点,虚线箭头表示数据在一个节点上面的传输,实现箭头表示在节点之间传输数据。

Reduce任务的数量不是由输入数据的大小决定的,而是独立指定的。在page191,你会见到,对于一个给定的作业怎么选择reduce任务的个数。

当有很多reducers时候,map任务分割他们的输出数据,每一个map任务为每一个reduce任务创建一个小数据块(由输出数据分割成的)。在每个小数据块中,可能会有很多keys(和他们相应的值),但是,每一个key对应的记录都在一个单独数据块中。这些小数据块可以被用户定义的分割函数来分割而成,但是通常情况下,我们使用默认的分割函数,用hash函数,这个很好使。

多个reduce任务的图标情况如下:图标形象的说明了,为什么他口语叫做 洗牌。Page177会详细讲到。

组合函数

许多mapreduce作业被集群上面的带宽限制,因此,需要最小化ma任务和reduce任务之间的数据传输。Hadoop允许用户指定一个组合函数,这个组合函数运行在map任务的输出部分,组合函数的输出数据成为reduce函数的输入数据。由于,组合函数是可选的,所以,hadoop不提供对于一个指定的map输出数据,组合函数会被调用几次,无论调用几次,结果是不变的。换句话说,组合函数,调用0次、1次很多次,reduce的输出数据是一样的。

组合函数约束函数的类型可能被用到。这样说,最好讲个例子。假设最高温度的例子,1950年的数据被两个map函数处理(这是因为他们被分割成不同的小块),假设第一个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])

并且如此的话,reduce函数的输出结果和之前相同。更简洁的说,我们可以用如下的表达式来说明找温度最大值函数的调用:

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

but:

mean(mean(0, 20, 10), mean(25, 15)) = mean(10, 20) = 15

组合函数不能代替reduce函数。但是它能够帮助减少map和reduce之间数据的传输量。仅仅这一个原因,就值得你考虑在mapreduce作业中是否可以用组合函数。

指明一个组合函数

回到java mapreduce程序,组合函数被定义是用reducer接口,对于这个程序,是和reducer函数一样的实现。唯一的变化是在jobconf上设置combiner类。

Example 2-7. Application to find the maximum temperature, using a combiner function for efficiency

public class MaxTemperatureWithCombiner {

public static void main(String[] args) throws IOException {

if (args.length != 2) {

System.err.println("Usage: MaxTemperatureWithCombiner <input path> " +

"<output path>");

System.exit(-1);

}

JobConf conf = new JobConf(MaxTemperatureWithCombiner.class);

conf.setJobName("Max temperature");

FileInputFormat.addInputPath(conf, new Path(args[0]));

FileOutputFormat.setOutputPath(conf, new Path(args[1]));

conf.setMapperClass(MaxTemperatureMapper.class);

conf.setCombinerClass(MaxTemperatureReducer.class);

conf.setReducerClass(MaxTemperatureReducer.class);

conf.setOutputKeyClass(Text.class);

conf.setOutputValueClass(IntWritable.class);

JobClient.runJob(conf);

}

}


Hadoop 

Hadoop提供一个mapreduceAPI,允许你用除了java语言以外的语言写mapreduce函数。Hadoop流使用unix 标准流作为hadoop和你的程序的接口。因此,你可以使用任何可以读写标准流的语言开发mapreduce程序。

流很自然的适合文字处理,(尽管在0.2版本他也可以处理二进制数据。)当用在文字模型时,他有一个面向行的数据。Map输入数据是通过标准输入方法给map函数的。这个方法是,一行一行的处理数据,并一行一行的输出数据到标准输出。一个map输出的键值对被写成一个的单独的tab分割文件。Reduce函数的输入数据也是这样的格式。Reduce函数以标准的读入方式读入这些行,hadoop框架保证数据按照key进行了排序,并且以标准形式输出。

Ruby

Example 2-8. Map function for maximum temperature in Ruby

#!/usr/bin/env ruby

STDIN.each_line do |line|

val = line

year, temp, q = val[15,4], val[87,5], val[92,1]

puts "#{year}\t#{temp}" if (temp != "+9999" && q =~ /[01459]/)

end

Hadoop管道

Hadoop的Pipes是Hadoop MapReduce的C++接口代称。不同于使用标准输入和输出来实现map代码和reduce代码之间的Streaming,Pipes使用套接字作为tasktracker与C++版本map函数或reduce函数的进程之间的通道,而未使用JNI。

我们将用C++重写贯穿本章的示例,然后,我们将看到如何使用Pipes来运行它。例 2-12 显示了用C++语言编写的map函数和reduce 函数的源代码。

…..


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值