第2章 关于MapReduce
MapReduce是一个并行的数据处理模型,作者将使用Java、Ruby和Python实现。
2.1 气象数据集
作者收集了气象的数据集,用于进行MapReduce分析。
数据格式
0057
332130 # USAF weather station identifier
99999 #WBAN weather station identifier
19500101 # observation date
8300 #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 (rneters)
1 # quality code
C
N
010000 # visibility distance ( meters)
1 # quality code
N
9
从1901年到2001年,每一年都有一个目录。
例如:1999年对应文件夹就包含下面的数据
% ls raw/1998 | 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
2.2 使用Unix工具来分析数据
在数据集中,每年全球气温最高纪录是多少?
作者先用Unix工具来分析,然后再用Hadoop进行对比
运行的结果为
1901 317
1902 244
1903 289
1904 256
1905 283
2.3 使用Hadoop来分析数据
可以先用MapReduce,在本地完成小规模测试后,就可以将作业部署到集群上运行。
2.3.1 map和reudce
MapReduce任务分为两个阶段,分别是map阶段和reduce阶段。每个阶段都是以键值对作为输入和输出。
map阶段的输入是NCDC数据,以文本进行输入,key是每一行的偏移量,value是改行的具体数据。
0067011990999991950051507004.. .9999999N9+00001+99999999999.. 0043011990999991950051512004...9999999N9+00221+99999999999..
0043011990999991950051518004...9999999N9-00111+99999999999...
0043012650999991949032412004...0500001N9+01111+99999999999.. 0043012650999991949032418004...0500001N9+00781+99999999999...
key是每一行的偏移量,value是改行具体的值
(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阶段提取出年份和气温两个字段。
(1950,0)
(1950,22)
(1950,-11)
(1949,111)
(1949,78)
将数据变为如下格式后,发送给reduce阶段
(1949,[111,78])
(1950,[0,22,-11])
Reduce阶段:遍历后面的list列表,找到最大的值。
得到如下结果
(1949,111)
(1950,22)
整个输入由下图表示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fp3c68gV-1627481872409)(Hadoop权威指南笔记1/1MapReduce执行流程.png)]
2.3.2 Java版本的MapReduce
Mapper类
public class MaxTemperatureMapper extends MapReduceBase
implements Mapper<LongWritable,Text,Text,IntWritable>{//LongWritable,Text 表示写入的key和value。而Text,IntWritable表示写出的key和value
private static final int MISSING = 9999;
//重写map方法
@Override
public void map(LongWritable key, Text value, Context context)
throws IOException,InterruptedException{
String line = value.toString();//转换为字符串
String year = line.substring(15,19);//年份在第15到19个字符
int airTemperature;//存储气温
if(line.charAt(87)=='+'){// +号不需要添加
airTemperature = Integer.parseInt(line.substring(88,92));
}else{
airTemperature = Integer.parseInt(line.substring(97,92));
}
String quality = line.substring(92,93);
if(airTemperature != MISSING && quality.matches("[01459]")){
// 将键值对写入 reduce端
context.write(new Text(year),new IntWritable(airTemperature));
}
}
}
Reduce类
public class MaxTemperatureReducer extends Reducer<Text,IntWritable,Text,IntWritable>{
@Override
public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException{
int maxValue = Integer.MIN_VALUE;
// reduce端接收的是一个迭代器。迭代器中保存着有相同key的value序列
for(IntWritable value : values){
maxValue = Math.max(maxValue,value.get());
}
context.write(key,new IntWritable(maxValue))
}
}
负责运行MapReduce作业
public class MaxTemperature{
public static void main(String[] args) throws Exception{
if (args.length != 2){
// 输入的参数必须是两个
System.exit(-1);
}
Job job = new Job();//新建一个Job
job.setJarByClass(MaxTemperature.class);//设置当前类为Job类
job.setJobName("Max temperature");//名称
//设置输入的路径
FileInputFormat.addInputPath(job, new Path(args[0]));
//设置输出路径
FileInputFormat.setOutputPath(job,new Path(args[1]));
//设置Mapper类
job.setMapperClass(MaxTemperatureMapper.class);
//设置Reduce类
job.setReducerClass(MaxTemperatureReducer.class);
//设置输出的key
job.setOutputKeyClass(Text.class);
//设置输出的value
job.setOutputValueClass(IntWritable.class);
// 规范的退出代码。成功为1,失败为0
System.exit(job.waitForCompletion(true)?0:1);
}
}
2.3.2.1 运行测试
调用Hadoop命令,第一个参数是类名,Hadoop启动JVM虚拟机来运行该类。
2.4 横向扩展
之前都只是在小规模的数据集上进行测试。
现在要扩展到大数据集,通过添加机器,然后运行在HDFS上,用YARN来进行资源管理。
2.4.1 数据流
定义术语:
(1)MapReduce作业(job) 是一个执行的工作单元: 包括输入数据、MapReduce程序和配置信息。
(2)一个作业(job)分为若干任务(task),比如说map任务和reduce任务。每个任务运行在一个节点(机器)上,通过YARN进行调度。
分片:
(1)MapReduce将输入数据分为等长的小数据块,称为输入分片(input split)。多个分片构建一个map任务。
(2)如果分片太大和太小都不太好。合理的分片大小趋向于HDFS的块大小,默认是128M。
(3)分区设置为块大小后,这样一个分区就是一个块,就不需要用到网络传输。
数据本地化优化:
(1)为了解决带宽,map任务执行的数据就是当前机器上的数据。称为数据本地优化
(2)但是有一种非常特殊的情况:对于数据A,我要执行数据A的map任务,但是有A的机器全在工作(执行其他map任务)。那么只能找别的机器运行,就需要用到网络带宽。
map任务
(1)map任务将输入写入本地磁盘,而非HDFS
(2)因为map产生的是中间结果,中间完成后就删除,没必要放入HDFS
(3)map在发送给reduce之前,会进行排序
reduce任务
(1)reduce任务的输入来自mapper的输出,这就需要用到网络带宽
(2)将数据接收,经过处理后,存储到HDFS中。
(3)输出的HDFS块的存储规律:第一个存储在本地节点,第二个存储其他机架的节点。
(4)若有多个reduce任务,map就会将输出进行分区,为每个reduce任务建一个分区。
(5)分区:由若干个相同的的key组成,这些key就是map端的输出key。
(6)分区数可以用户自定义,默认是通过哈希数来分区,比较高效。
(7)多个map端 和 多个reduce端 之间的多分区传递方式,就称之为shuffle
没有reduce任务
(1)直接将map输出写入HDFS
2.4.2 Combiner函数
Map端传递给Reducer端需要用到网络带宽,这限制了速度。
所以可以先 map => combiner => reducer。
combiner的工作过程和reduce类似,可以减轻reduce的任务。
例子
第一个map端的输出
(1950, 0)
(1950,20)
(1950,10)
第二个map端的输出
(1950,25)
(1950,15)
不使用combiner的 reducer 端的输入
(1950,[0,20,10,25,15])
不使用combiner的 reducer端的输出
(1950,25)
使用combiner的 reducer端的输入
(1950,[20,25])
使用combiner的 reducer端的输出
(1950,25)
用下列表达式来表示,两个map对于两个combiner,combiner执行max
max(0,20,10,25,15) = max(max(0,20,10),max(25,15)) = max(20,25) = 25
两个map对于两个combiner,combiner执行mean。显然这个结果是错的,所以combiner并不能替代Reducer,只是一个辅助工具。
mean(mean(0,20,10),mean(25,15)) = mean(10,20)= 15
指定一个Combiner
public class MaxTemperatureWithCombiner{
public static void main(String[] args) throws Exception{
if(args.length !=2){
// 退出程序
System.exit(-1);
}
Job job = new Job();
job.setJarByClass(MaxTemperatureWithCombiner.class);
job.setJobName("Max temperature");
FileInputFormat.addInputPath(job,new Path(args[0]));
FileOutputFormat.setOutputPath(job,new Path(args[1]));
job.setMapperClass(MaxTemperatureMapper.class);
//设置一个combiner
job.setCombinerClass(MaxTemperatureReducer.class);
job.setReducerClass(MaxTemperatureReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
// 规范的退出代码。成功为1,失败为0
System.exit(job.waitForCompletion(true)?0:1);
}
}
2.4.3 运行分布式的MapReduce作业
该程序可以直接在集群上运行
将在第6章分析在集群上运行程序的机制
2.5 Hadoop Streaming
Hadoop除了运行Java之外,还允许Ruby和Python等语言。
Streaming适合文本处理,输入是偏移量,文本。输出是年份,最高气温。
2.5.1 Ruby版本
pass
2.5.1 Python版本
作者使用的是Python2,现在以及普及Python3了,所以这里省略。