1.map和reduce
MapReduce任务编写分为两个阶段:map阶段和reduce阶段,每个阶段都以键值对作为输入和输出。对于NCDC数 据找出每年的最高气温,map阶段输入为原始数据以偏移量为键,每行数据为值,输出每条记录的年份与温度的键值对,如图所示:
图1 map阶段输入数据格式
图2 map阶段输出数据格式
reduce阶段的输入为map阶段的输出,该输出经过处理后将相同键对应的值排列在一起组成新的键值对作为reduce输入(shuffle),含有相同键的元组会被传输到同一个Reducer任务中,reduce的输出为计算得到的年份与该年气温最大值的键值对形式,如图所示:
图3 reduce输入数据格式
图4 reduce输出数据格式
读取NCDC数据找出每年的最高气温的整体流程如下所示:
图5 NCDC数据计算每年最高气温整体流程图
- 读取原始数据,并将数据转换成偏移量为键,每行内容为值的数据形式(input);
- 将步骤一得到的键值对数据进一步处理,提取出每行中的数据一一对应为年份为键,温度为值的数据形式(map);
- 对map阶段输出的键值对按键进行排序处理,相同键对应的值放在一起(shuffle);
- 对每个键对应的值列表取最大值,最后得到年份和该年最高温度的键值对形式(reduce);
- 输出reduce结果(output).
MapReduce应用分为以下几个阶段
- map:从存储系统(如HDFS)中读取数据
- sort:根据键对map任务中的输入数据进行排序
- shuffle:划分排序后的数据并在集群节点中重新分配
- merge:在每个节点上合并由mapper发送的输入数据
- reduce:读取合并后的数据并把它们集成为一个结果
其中map和reduce需要编写,其他阶段由MapReduce框架进行处理。
2.MapReduceJava的处理程序
(1)map操作类
map函数的操作需要声明类并继承Mapper类,代码如下:
public class TemperatureMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
private static final Integer MISSING = 9999;
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
String line = value.toString(); //读取输入数据的一行数据
String year = line.substring(15, 19); //在每一行中提取出年份信息
int airTemperature;
if (line.charAt(87) == '+'){
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]")){
//保存输出数据,调用context对象的write方法,输出map的结果,
//第一个参数为输出的键,第二个参数为输出的值
context.write(new Text(year), new IntWritable(airTemperature));
}
}
}
Mapper类是一个泛型类,该泛型包含四个形参类型,分别对应map方法的输入键、map方法的输入值、map方法的输出键、map方法的输出值。由于NCDC数据输入键是一个数据对应偏移量,因此选用LongWritable作为输入键类型,而数据输入的值为一行文本,选用Text类型,map方法的输出为(年份,温度)的形式,所以输出键为Text类型,输出值为IntWritable类型。Hadoop对Java的数据类型进行封装,并放在org.apache.hadoop.io包中,该例中的LongWritable、Text、IntWritable分别对应Java中的long、String、int类型。map方法中还提供了Context类进行输出的写入。
map方法是对输入数据变换,输入和输出始终是一一对应的关系,即输入一行数据,输出的是对该行数据的处理结果。
(2)reduce操作类
public class TemperatureReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
int maxValue = Integer.MIN_VALUE;
//选出键对应所有值中的最大值
for (IntWritable value : values) {
maxValue = Math.max(maxValue, value.get());
}
//将键与对应的最大值写入context
context.write(key, new IntWritable(maxValue));
}
}
reduce操作需要类继承Reducer类,该类同样是一个包含四个参数类型的泛型类,四个泛型类型分别对应reduce方法的输入键、reduce方法的输入值、reduce方法的输出键、reduce方法的输出值,其中,前两个泛型类型必须和map方法对应的输出键、值类型相对应。由于每条数据中,一个键对应多个值,即值的集合,因此在重写的reduce方法参数中值类型为Iterable。
(3)MapReduce运行
MapReduce运行类如下所示:
public class TempratureTest {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
// 新建配置类,参数设为true,则调用默认配置文件
Configuration conf = new Configuration(true);
// 得到Job对象实例,并传入conf
// 也可以不创建Configuration对象,并且getInstance方法不传参,则会调用默认配置文件
// Job job = Job.getInstance();
Job job = Job.getInstance(conf);
// 设置job相关内容
job.setJarByClass(TempratureTest.class);//设置job对应的类型
job.setJobName("Max Temperature");//设置job名称
FileInputFormat.addInputPath(job, new Path(args[0]));//设置job处理数据的输入路径
FileOutputFormat.setOutputPath(job, new Path(args[1]));//设置job处理结果的输出路径
job.setMapperClass(TemperatureMapper.class);//设置mapper类
job.setReducerClass(TemperatureReducer.class);//设置reducer类
job.setOutputKeyClass(Text.class);//输出键类型
job.setOutputValueClass(IntWritable.class);//输出值类型
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
3.数据流
MapReduce的作业Job是客户端需要执行的一个工作单元,它包括输入数据、MapReduce程序和配置信息。Hadoop将作业分成若干个任务(task)来执行,任务分为map和reduce两类。Hadoop将MapReduce的输入数据划分成等长的小数据块,称为分片,并为每个分片构建一个map任务,map任务处理由用户定义的方法决定。对于分片的大小,一个合理的分片大小趋向于HDFS的一个块大小,默认为128M,因为它是确保可以存储在单个节点上的最大输入块的大小。如果分片垮两个数据块,则HDFS节点基本上不可能同时存储这两个数据块。
单个reduce任务流如下所示:
图6 单个reduce任务
多个reduce任务流如下所示:
图7 多个reduce任务
从图6、图7可以看出对于多个reduce任务的MapReduce,map操作后会对输出进行分区(partition),为每个reduce任务创建一个分区,并且在map和reduce之间的shuffle操作时,一个reduce任务的输入来自多个map任务。
参考文献
[1] Tom While, Hadoop权威指南(第四版)[M].北京, 清华大学出版社, 2017.
[2] Benoy Antony, Konstantin Boundnink, Chryl Adams, et al. Hadoop大数据解决方案[M].北京, 清华大学出版社, 2017.