第二章mapreduce(翻译粗略,优化中)

第二章 mapreduce

mapreduce是一个数据处理的编程模型。这个模型是简单的,虽然简单,但是她能够表达有用的编程。Hadoop可以运行用各种语言写的mapreduce程序,本章中,我们将看用javarubypythonc++写的表示同一意思的程序。Mapreduce本质上是平行的,因此可以把大量的数据分析分配到他们所支配的足够的机器。Mapreduce是处理大数据的,那么现在,让我们来看一个例子。

一个天气预报数据

例如,我们讲些一个程序来处理天气信息。天气感应器在地球的很多地方,每个小时都搜集大量的数据信息,这是一个mapreduce分析的很好的候选者,因为他是一个半结构化的数据,并且是面向记录的。

数据格式化

我们即将要使用的是数据是来自ncdc的。这些数据是基于行的ascii格式的数据,数据的每一行都是一条记录。这种格式的数据支持很多气象学元素,许多元素是可选的,或者一些数据元素的长度是可变的。简单起见,我们将仅关注基本的元素,例如气温,气温总是呈现的,并且宽度也是一定的。

例子21展示了一行例子,高亮显示了一些重要字段。这一行数据已经被分割成了多行,以便显示每一个字段:在真实的文件中,这些字段都是在一行中的,是没有分隔符的。

Example 2-1. Format of a National Climate Data Center record

0057

332130 # USAF weather station identifier

99999 # WBAN weather station identifier

19500101 # observation date

0300 # observation time

4

+51317 # latitude (degrees x 1000)

+028783 # longitude (degrees x 1000)

FM-12

+0171 # elevation (meters)

99999

V020

320 # wind direction (degrees)

1 # quality code

N

0072

1

00450 # sky ceiling height (meters)

1 # quality code

CN

010000 # visibility distance (meters)

1 # quality code

N9

-0128 # air temperature (degrees Celsius x 10)

1 # quality code

-0139 # dew point temperature (degrees Celsius x 10)

1 # quality code

10268 # atmospheric pressure (hectopascals x 10)

1 # quality code

这些数据是根据日期和气象站来组织的。从1901年到2001年的每一年都有文件目录,每一个目录都包含当年每一个气象站的记录信息的压缩文件。例如,如下是1990年的第一次记录。

% ls raw/1990 | head

010010-99999-1990.gz

010014-99999-1990.gz

010015-99999-1990.gz

010016-99999-1990.gz

010017-99999-1990.gz

010030-99999-1990.gz

010040-99999-1990.gz

010080-99999-1990.gz

010100-99999-1990.gz

010150-99999-1990.gz

因为有数以万计的气象站,整个数据库是有大量的相对小的文件组成的。然而,与其相比,处理少量的相对较大的文件更容易一些,更高效一些,因此,这些气象数据都被处理成这样了,这样每年的数据都被串成了一个单独的文件(笔者注:而非一个一年为文件名的文件夹里面),详见附录C

UNIX工具分析数据

在数据库中每一年的最高温度是多少?首先我么会不用hadoop解决这个问题,这样可以为我们提供一个性能参考线,也可以验证我们的结果。

。。。。

为了加快运算,我们需要并行运算程序的各个部分。理论上,这是很直截了当的,我们用不同的处理器处理不同年份的数据,我们用一个机器上面的所有可用的硬件设备。然而,这样是有一些问题的。

首先,把这个工作分割成相等的小任务不是那么容易的也不是明显的。在这种情况下,每一年的文件大小会差别很大,因此,有些处理器可能比其它的处理器提前很久就完成任务了。即便,提前完成的处理器继续处理其他的小任务,整个任务的计算时间是由最长的文件决定的。那么,此时,更好的一个方法就是将任务分割成固定大小的任务,并且把他们分派给一个处理器,尽管这样会需要做更多的工作。

其次,把不同的处理器处理出来的结果整合成一个结果可能需要更多的处理。每年的结果和其它年的结果是相互独立的,这些结果可以组合起来,并按照年份拍顺序。如果使用固定大小的任务块的方法(笔者注:即hadoop),那么这种对结果的组合会更加微妙些。例如,每年的数据会被典型的分割成几个小块,每个小块都被单独的处理。然后,我们比较每个小块中最高温度的大小,这样的话,对于每年来说,最终步骤就是寻找这些小块中的最大值。

第三点,你仍然被局限在一个机器上面处理器的处理能力。如果,你计算的最快速度是20分钟,那么他就是20分钟,你不能让他更快了。同时,有些数据可能会大到一个机器不能处理的程度。当我们使用多个机器的时候,很多的其他因素就随之而来了,主要就要处理这些机器之间的协调性和可靠性。谁来做整个的全局工作,谁来处理失败的计算?

尽管,多线程并行处理是稳定的,实际上他确是很混乱的。那么,用一个框架,像Hadoop来处理这些事情,将会你对大有帮助。

Hadoop分析数据

为了充分利用hadoop提供的多线程并行处理,我们需要用mapreduce工作来表达我们的查询过程。在一些本地的、小范围的练习之后,我们将能够将其运行在一大群机器上面。

MapReduce

Mapreduce靠把整个处理过程分割成两部来工作:map过程和reducers过程。每个过程都有键值对作为输入和输出,键值对的类型是由编程人员决定的。程序员同时需要实现两个函数,map函数和reduce函数。

Map过程的输入数据就是原始的未经过加工的NCDC数据。我们选择文本型格式的数据,让他的每一行作为数据库的文本值。数据库的键是文本值在这个文件中的行号,但是我们是不用键的,我们忽略掉他。

我们的map函数是很简单的,我们从数据中筛选出年份和气温,因为这两个才是我们关注的字段,map过程是数据与处理过程,这样处理过的数据,reducer可以在这个基础上面进行处理:找到每年的最高气温。Map函数同样是一个丢掉无效数据的好地方:这里我们筛选出丢失的数据,有嫌疑的数据和错误的数据。

为了能够明了的看到map工作的过程,请看以下的用行表示的输入数据。(一些不被用到的列被删除了,用椭圆来表示)

0067011990999991950051507004...9999999N9+00001+99999999999...

0043011990999991950051512004...9999999N9+00221+99999999999...

0043011990999991950051518004...9999999N9-00111+99999999999...

0043012650999991949032412004...0500001N9+01111+99999999999...

0043012650999991949032418004...0500001N9+00781+99999999999...

这些每行的数据以键值对的形式提供给map函数。

(0, 0067011990999991950051507004...9999999N9+00001+99999999999...)

(106, 0043011990999991950051512004...9999999N9+00221+99999999999...)

(212, 0043011990999991950051518004...9999999N9-00111+99999999999...)

(318, 0043012650999991949032412004...0500001N9+01111+99999999999...)

(424, 0043012650999991949032418004...0500001N9+00781+99999999999...)

键是每行文字在每个文件中的行号,键在我们的map函数里面是忽略的。Map函数仅仅提取出年和气温(用黑体字表示了),并且把他们作为输出数据传递出去。(气温已经被转为整数了):

(1950, 0)

(1950, 22)

(1950, −11)

(1949, 111)

(1949, 78)

Map函数的输出数据会被mapreduce框架处理,然后这些数据被发送给reduce函数。在这个过程中,会对这些键值对按照键进行分类和分组。下面,我们继续我们的例子,reduce函数会得到如下的输入数据:

(1949, [111, 78])

(1950, [0, 22, −11])

每年都有一个列表列出了这一年的所有气象站的气温。所有的reduce函数要做的事情就是遍历列表筛选出最大的数据。

(1949, 111)

(1950, 22)

这就是最终的数据结果:全球每年的最高气温。

JAVA Mapreduce

已经知道了mapreduce的运行过程,下一步就是用代码写出来了。我们需要做三件事情,一个map函数,一个reduce函数,和一些运行这个工作的代码。Map函数实现mapper接口,里面声明了map()方法。例子2—3展示了map函数的实现。

Example 2-3. Mapper for maximum temperature example

import java.io.IOException;

import org.apache.hadoop.io.IntWritable;

import org.apache.hadoop.io.LongWritable;

import org.apache.hadoop.io.Text;

import org.apache.hadoop.mapred.MapReduceBase;

import org.apache.hadoop.mapred.Mapper;

import org.apache.hadoop.mapred.OutputCollector;

import org.apache.hadoop.mapred.Reporter;

public class MaxTemperatureMapper extends MapReduceBase

implements Mapper<LongWritable, Text, Text, IntWritable> {

private static final int MISSING = 9999;

public void map(LongWritable key, Text value,

OutputCollector<Text, IntWritable> output, Reporter reporter)

throws IOException {

String line = value.toString();

String year = line.substring(15, 19);

int airTemperature;

if (line.charAt(87) == '+') { // parseInt doesn't like leading plus signs

airTemperature = Integer.parseInt(line.substring(88, 92));

} else {

airTemperature = Integer.parseInt(line.substring(87, 92));

}

String quality = line.substring(92, 93);

if (airTemperature != MISSING && quality.matches("[01459]")) {

output.collect(new Text(year), new IntWritable(airTemperature));

}

}

}

Mapper接口是一个普通的类型,它有四个普通类型的参数,输入键,输入值,和map函数输出值的类型。以此气象数据为例,输入键是long型行号,输入值是一行数据,输出的键是一个年份,输出的值是一个气温(整数型的)。Hadoop提供了他自己的一套基本数据类型,这些数据类型是转为网络序列化所优化的,而不是采用java数据类型。这里,我们使用了LongWritable,相当于java里面的long类型,Text类型(相当于java里面的string类型),IntWritable(相当于java里面的integer类型)。

Map()方法传递一个键和一个值。

Reducer函数如下:

Example 2-4. Reducer for maximum temperature example

import java.io.IOException;

import java.util.Iterator;

import org.apache.hadoop.io.IntWritable;

import org.apache.hadoop.io.Text;

import org.apache.hadoop.mapred.MapReduceBase;

import org.apache.hadoop.mapred.OutputCollector;

import org.apache.hadoop.mapred.Reducer;

import org.apache.hadoop.mapred.Reporter;

public class MaxTemperatureReducer extends MapReduceBase

implements Reducer<Text, IntWritable, Text, IntWritable> {

public void reduce(Text key, Iterator<IntWritable> values,

OutputCollector<Text, IntWritable> output, Reporter reporter)

throws IOException {

int maxValue = Integer.MIN_VALUE;

while (values.hasNext()) {

maxValue = Math.max(maxValue, values.next().get());

}

output.collect(key, new IntWritable(maxValue));

}

}

第三段代码执行mapreduce工作

Example 2-5. Application to find the maximum temperature in the weather dataset

import java.io.IOException;

import org.apache.hadoop.fs.Path;

import org.apache.hadoop.io.IntWritable;

import org.apache.hadoop.io.Text;

import org.apache.hadoop.mapred.FileInputFormat;

import org.apache.hadoop.mapred.FileOutputFormat;

import org.apache.hadoop.mapred.JobClient;

import org.apache.hadoop.mapred.JobConf;

public class MaxTemperature {

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

if (args.length != 2) {

System.err.println("Usage: MaxTemperature <input path> <output path>");

System.exit(-1);

}

JobConf conf = new JobConf(MaxTemperature.class);

conf.setJobName("Max temperature");

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

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

conf.setMapperClass(MaxTemperatureMapper.class);

conf.setReducerClass(MaxTemperatureReducer.class);

conf.setOutputKeyClass(Text.class);

conf.setOutputValueClass(IntWritable.class);

JobClient.runJob(conf);

}

}

/**数据流

Hadoopmapreduce的输入数据分割成固定大小的片,叫做输入小片或者小片。Hadoop为每一个小片创建一个map任务,map任务执行用户指定的map函数计算小片中的每一条记录。

Map的输出键值是保存在本地的,reduce的输出值是保存在网络上的。第一个复制品是存储在本地节点上,另一个复制品是存储在另一个节点上。

Page29

Reduce的任务数不是由输入的数据组织的,而是单独指定的。以后的章节中会教你对于一个给定的工作,怎样指定reduce的任务个数。

当有很多reducers的时候,map 任务把它的输出分成小部分,每个map任务给每个reduce任务创造一个小分区,但是记录的额任何一个键都是在一个单独的分区内。分割过程可以由用户定义好的分割函数来控制,但是,通常情况下是默认的分割方法,即用hash函数存储键。

P33

Hadoop

**/

Page 28

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值