一、MapReduce概述
MapReduce是大数据离线计算的一种处理范式。它的基本概念就是“分而治之”:将单个问题分解成多个独立的子任务,再将子任务的结果汇聚成最终结果。
在 MapReduce 中,它会先把样本分成一段段能够令单台计算机处理的规模,然后让多台计算机同时进行各段样本的整理和统计,每执行完一次统计就对映射统计结果进行规约处理,最终完成大规模的数据规约。
MapReduce 的含义分为两步:Map 和 Reduce (映射和规约) 。上述过程第一阶段的整理工作就是"映射",把数据进行分类和整理,得到一个相比于源数据小很多的结果。第二阶段的工作往往由集群来完成,整理完数据之后,将这些数据进行总体的归纳,映射的结果将会进一步缩略成可获取的统计结果。
1. 键值对处理模式
键值对是 MapReduce 操作的基础,MapReduce 的处理过程大致如下:
{K1,V1} ——> {K2,List<V2>} ——> {K3,V3}
- MapReduce 作业的 map 方法的输入是一系列键值对 K1,V1
- map 方法的输出是一系列键以及与之关联的值列表 K2,List<V2>。(每个mapper 仅仅输出一系列单个的键值对,通过 shuffle 方法组合成键与值列表)
- K2、V2 作为 Reduce 的输入,最终输出另一串键值对 K3、V3
二、MapReduce API
由于MapReduce现在逐渐被其他计算框架所取代,这里主要介绍 MapReduce 的思想和基本使用方式
1. Mapper
Hadoop 提供了 Mapper 基类,一般情况需要自定义 mapper类继承该基类并重写指定方法:
public class Mapper<K1, V1, K2, V2> {
protected void map(KEYIN key, VALUEIN value,
Context context) throws IOException, InterruptedException {
context.write((K2) key, (V2) value);
}
}
map方法以输入的键值对作为参数,Context提供Hadoop框架多种通信机制,其中之一是输出 map 或 reduce 方法的结果
mapper示例:
public class WordCountMapper extends Mapper<Object, Text, Text, IntWritable> {
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();
@Override
protected void map(Object key, Text value, Mapper<Object, Text, Text, IntWritable>.Context context) throws IOException, InterruptedException {
String[] words = value.toString().split(" ");
for (String str: words){
word.set(str);
context.write(word, one);
}
}
}
在该示例中,该作业使用 TextInputFormat 作为输入数据的格式。默认情况下,这种格式的键指的是文件的行号,值指的是内容。
对于输入源中的每行文本,mapper 都会执行一次,每次运行会把该行内容切分成词语,并使用 Context 以 <word,1>格式输出新的键值对
2. Reducer
Reducer基类:
public class Reducer<K2, V2, K3, V3> {
protected void reduce(K2 key, Iterable<V2> values, Context context
) throws IOException, InterruptedException {
for(V2 value: values) {
context.write((K3) key, (V3) value);
}
}
}
reducer示例:
public class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException {
int total = 0;
for (IntWritable value : values) {
total++;
}
context.write(key, new IntWritable(total));
}
}
MapReduce会将每个键对应的值收集起来(暂不描述该过程),并对每个键执行一次 reducer
3. Hadoop自带的 mapper 和 reducer
Hadoop提供了几种常见的 Mapper 和 Reducer 的实现:
- InverseMapper:倒转键值对
- TokenCounterMapper:对输入的每行文本的离散令牌进行计数
- IntSumReducer:输出每个键对应整数值列表的总和
- LongSumReducer:输出每个键对应长整数值列表的总和
这里的 TokenCounterMapper 和 IntSumReducer 可以取代上述的 WordCountMapper 和 WordCountReducer
4. combiner
由于 mapper 的输出量通常很大,为了减少数据的传输量提高性能,可以添加 combiner 对数据进行早期的聚合以减少传输的数据量。
combiner 同样实现自 Reduce类,要注意的是,Hadoop 不保证 combiner 被执行的次数!可能根本不执行或被执行多次。
使用 combiner 需要对上面的 reducer 进行修改:
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException {
int total = 0;
for (IntWritable value : values) {
total += val.get();
}
context.write(key, new IntWritable(total));
}
三、MapReduce原理
MapReduce的整体工作流程如下:
- 启动:驱动程序开启本地主机与 JobTracker 的通信。JobTracker 负责作业调度和执行,负责与 NameNode 通信,并对储存在 HDFS 上的数据相关的所有交互进行管理。
- 输入分块:HDFS 文件通常被分成至少 64MB 的数据块,JobTracker 会将每个数据块分配给一个 map 任务。
- 任务分配:JobTracker 确定所需的 map 任务数后,会去检查集群中的主机数,正在运行的 TaskTracker 数以及可并发执行的 map 任务数(用户自定义配置)。JobTracker 会查看各个输入数据块在集群中的分布位置,并尝试建立一个合理的执行计划,使 TaskTracker 尽可能处理位于相同物理主机上的数据块以提高性能
由于默认情况下,每个数据块会被复制到三台不同的主机,所以在本地处理大部分数据块任务计划的可能性比想象的高
- 任务启动:每个 TaskTracker 开启一个独立的 Java虚拟机来执行任务:
- 如果集群有能力一次性执行所有任务,每个TaskTracker 会将分块复制到本地文件系统,启动所有任务
- 否则 JobTracker 将维护起一个挂起任务队列,并在节点完成最初分配的 map 任务后,将挂起任务分配给各个节点。
- 监视 JobTracker :JobTracker 会不断与 TaskTracker 交换心跳和状态消息,跟踪进度和问题,并收集指标。
- mapper 输入和执行:执行我们自定义的 mapper
- shuffle:在 map 和 reduce 之间,会有一个 shuffle 阶段,对数据进行整理
- 分块:Reduce 接口的隐形保证:给定键相关的所有值会被提交到同一个 reducer。因此需要将 mapper 的输出结果分块,使其分别传入相应的各个 reducer。分块文件保存在本地节点的文件系统
- reducer类的输入:reducer的 TaskTracker 从 JobTracker 接收更新,指明集群中哪些节点承载着输出分块。 TaskTracker 从各个节点获取分块,并合并为一个文件反馈给 reduce 任务。
- reducer的执行和输出
- 关机
四、MapReduce其他功能
- Hadoop Streaming:可以用任何使用标准输入和输出的脚本语言运行 map 和 reduce 任务。可以在对数据进行前期分析时使用 Streaming, 在实现运行于产品集群的作业时使用 Java 语言。
- ChainMapper:可以顺序执行多个 mapper,通过它执行多个 map 型任务再应用于 reducer。可以将复杂的 map 任务分解成一系列较小且更有针对性的 map 任务。
- Distributed Cache:Hadoop 提供的分布式缓存,可为所有任务提供共享的引用数据。
- 计数器:可以运用计数器、任务状态信息和 debug 日志进行高效的作业分析。