本次主要介绍MapReduce,分为上篇,下篇两个篇章
- 上篇介绍MapReduce计算模型,MapReduce编码规范及测试,程序运行模式。
- 下篇介绍MapReduce序列化,MaoReduce排序初步,Mapreduce 的分区。
1. MapReduce的输入和输出
- 输入输出简介
MapReduce 框架运转在key,value键值对上,也就是说,框架把作业的输入看成是一组key,value键值对,同样也产生一组key,value键值对作为作业的输出,这两组键值对可能是不同的。
一个 MapReduce 作业的输入和输出类型如下图所示:可以看出在整个标准的流程中,会有三组key,value键值对类型的存在。
流程解析
Mapper任务执行过程详细解析
第一阶段是进行逻辑切片, 形成切片规划。 默认情况下, Split size = Block size。 每一个切片由一个MapTask 处理。 ( getSplits)。
第二阶段是解析key,value对。 默认规则是把每一行文本内容解析成键值对。key是每一行的起始位置(单位是字节),value是本行的文本内容。 (TextInputFormat)
第三阶段是调用 Mapper 类中的 map 方法。 上阶段中每解析出来的一个k,v,调用一次 map 方法。 每次调用 map 方法会输出零个或多个键值对。
第四阶段是对第三阶段输出的键值对进行分区。 默认是只有一个区。 分区的数量就是 Reducer 任务运行的数量。默认只有一个Reducer 任务。分区规则为key.hashCode/ReduceTasks的值,按照这个值决定落在哪个分区
第五阶段是键值对进行排序。首先,按照键进行排序,对于键相同的键值对,按照值进行排序。如果有第六阶段,那么进入第六阶段;如果没有,直接输出到文件中。
第六阶段是对数据进行局部聚合处理,也就是 combiner 处理。 键相等的键
值对会调用一次 reduce 方法。经过这一阶段,数据量会减少。 本阶段默认
是没有的。
Reducer 任务执行过程详解
第一阶段是 Reducer 任务从 Mapper 任务复制其输出的键值对。Mapper 任务可能会有很多,因此 Reducer 会复制多个 Mapper 的输出。
第二阶段把复制的数据进行合并,排序。
第三阶段是对排序后的键值对调用 reduce 方法,实现业务逻辑。 键相等的键值对调用一次reduce 方法,每次调用会产生零个或者多个键值对。最后把这些输出的键值对写入到HDFS 文件中。
在整个 MapReduce 程序的开发过程中,我们最大的工作量是覆盖 map 函数
和覆盖 reduce 函数
2. MapReduce的序列化
- 需求:对于记录用户手机信息的文件,得出统计每一个用户(手机号)所耗费的总上行流量、下行流量,总流量结果,源文件内容如下
1363157985066 手机号码13726230503 00-FD-07-A4-72-B8:CMCC 120.196.100.82 i02.c.aliimg.com 24 27 上行流量2481 下行流量24681 200
1363157995052 13826544101 5C-0E-8B-C7-F1-E0:CMCC 120.197.40.4 4 0 264 0 200
1363157991076 13926435656 20-10-7A-28-CC-0A:CMCC 120.196.100.99 2 4 132 1512 200
1363154400022 13926251106 5C-0E-8B-8B-B1-50:CMCC 120.197.40.4 4 0 240 0 200
1363157993044 18211575961 94-71-AC-CD-E6-18:CMCC-EASY 120.196.100.99 iface.qiyi.com 视频网站 15 12 1527 2106 200
实现思路:实现自定义的 bean 来封装流量信息,使用手机号码作为Key,Bean作为value。这个Bean的传输需要实现可序列化,Java类中提供的序列化方法中会由很多冗余信息(继承关系,类信息)是我们不需要的,而这些信息在传输中占据大量资源,会导致有效信息传输效率减低。因此我们需要实现MapReduce的序列化接口Writable,自定义方法实现。
代码实现
- FlowBean类,自定义的JavaBean,在此类中实现序列化方法。
package com.hadoop.mapReduce01;
import org.apache.hadoop.io.Writable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class FlowBean implements Writable {
//上行流量
private long upFlow;
//下行流量
private long downFlow;
//总流量
private long sumFlow;
public FlowBean() { }
//构造方法1
public FlowBean(long upFlow, long downFlow, long sumFlow) {
this.upFlow = upFlow;