大数据Hadoop之MapReduce高级编程

内容提要

  • 自定义数据类型
  • 自定义输入/输出
  • 自定义Combiner/Partitioner
  • 组合式计算作业
  • MapReduce的特性
  • MapReduce成绩分析系统解析

一、自定义数据类型

Hadoop提供了很多内置的数据类型,常用的是Java基本类型的Writable封装,例如:

FloatWritable:浮点数

IntWritable:整型数

LongWritable:长整型数

Text:使用UTF-8格式存储文本

这些数据类型都实现了WritableComparable接口,可以进行网络的传输和文件存储,以及大小的比较。

WritableComparable接口的定义如下:

public interface WritableComparable<T>

{

public void readFields(DataInput in);

public void write(DataOutput out);

public int compareTo(T other);

}

相较于Writable接口,WritableComparable接口多了一个compareTo()方法,实现数据的比较。

自定义数据类型可以实现两个接口。一个是Writable接口,需要实现其write()和readFields()方法,以便数据能被序列化后完成网络传输或文件输入/输出;一个是WritableComparable接口,需要实现write()、readFields()、CompareTo()方法,以便数据能用作key或比较大小。

二、自定义输入/输出

RecordReader与RecordWriter

2.1 RecordReader

对于每一种数据输入格式,都需要有一个对应的RecordReader。RecordReader主要用于将一个文件中的数据记录拆分成具体的key-value键值对,并传送给Map节点作为输入数据。

RecordReader的定义如下:

public abstract class RecordReader<KEYIN, VALUEIN> implements Closeable

{

//由一个InputSplit初始化

public abstract void initialize(InputSplit split, TaskAttemptContext context)

//读取分片中的下一个key-value对

public abstract boolean nextKeyValue();

//获取当前key的值

public abstract KEYIN getCurrentKey();

//获取当前value的值

public abstract VALUEIN getCurrentValue();

//跟踪读取分片的进度

public abstract float getProgress();

public abstract void close();

}

2. 2 RecordWriter

与输入格式中的RecordReader类似,每一种数据输出格式也需要有一个对应的RecordWriter。RecordWriter主要用于将作业输出结果按照格式写到文件中。RecordWriter的定义如下:

public abstract class RecordWriter<K, V>

{

public abstract void write(K key, V value);

public abstract void close(TaskAttemptContext context);

}

在一些特定情况下用户需要定制自己的数据输入格式和RecordReader。自定义输入格式步骤如下:

(1) 定义一个继承自InputFormat的类,一般继承FileInputFormat类即可。

(2) 实现其createRecordReader()方法,返回一个RecordReader。

(3) 自定义一个继承RecordReader的类,实现其initialize()、getCurrentKey()、getCurrentValue()方法,选择性实现nextKeyValue()。

用户可以根据应用程序的需要自定义数据输出格式与RecordWriter。自定义输出格式步骤如下:

(1) 定义一个继承自OutputFormat的类,一般继承FileOutputFormat类即可。

(2) 实现其getRecordWriter()方法,返回一个RecordWriter。

(3) 自定义一个继承RecordWriter的类,实现其writer()方法,针对每个key-value键值对写入文件数据。

三、自定义Combiner/Partitioner

3.1 自定义Combiner

用户可以根据需要自定义Combiner,以减少Map阶段输出中间结果的数据量,降低数据的网络传输开销。自定义Combiner步骤如下:

(1) 定义一个继承自Reducer的类。

(2) 实现其reduce()方法。

(3) 通过job.setCombinerClass()来设置自定义的Combiner。

3.2 自定义Partitioner

自定义Partitioner步骤如下:

(1) 定义一个继承自Partitioner的类。

(2) 实现其getPartition()方法。

(3) 通过job.setPartitionerClass()来设置自定义的Partitioner。

四、组合式计算作业

4.1 迭代式计算

MapReduce迭代式计算的中心思想,类似for循环,前一个MapReduce的输出结果,作为下一个MapReduce的输入,任务完成后中间结果都可以删除。例如,现在有3个MapReduce子任务。其中,子任务1的输出目录Outpath1将作为子任务2的输入目录,而子任务2的输出目录Outpath2又作为子任务3的输入目录。

4.2 依赖关系组合式计算

对于依赖关系组合式计算,同样是需要多个MapRedcue才能完成任务,却不是顺序执行。例如,MapReduce有3个子任务job1,job2,job3,其中job1和job2相互独立,job3要在job1和job2完成之后才执行,就称作job3依赖于job1和job2。

Hadoop为这种依赖关系组合式计算提供了一种执行和控制的机制。Hadoop通过Job和JobControl类提供具体的编程方法。Job除了维护子任务的配置信息,还维护子任务的依赖关系。而JobControl控制整个作业流程,把所有的子任务作业加入到JobControl中,执行JobControl的run()方法即可运行程序。

4.3 链式计算

一个MapReduce作业可能会有一些前处理和后处理步骤,一个较好的办法就是在核心的MapReduce之外,增加一个辅助的Map过程,然后将这个辅助的Map过程和核心的Mapreudce过程合并为一个链式的Mapreduce,从而完成整个作业,这就是链式计算。简单来说,链式计算就是前面用多个Mapper处理任务,最后用一个Reducer输出结果。

五、MapReduce的特性

5.1 计数器

计数器是用来记录Job的执行进度和状态的,它的作用可以理解为日志。用户可以在程序的某个位置插入计数器,记录数据或者进度的变化情况。计数器还可辅助诊断系统故障。如果需要将日志信息传输到Map或Reduce任务,通常是尝试传输计数器值以监测某一特定事件是否发生。对于大型分布式作业而言,使用计数器更为方便。首先,获取计数器值比输出日志更方便;其次,根据计数器值统计特定事件的发生次数要比分析一堆日志文件容易得多。

5.1.1 内置计数器

1. MapReduce任务计数器

2. 文件系统计数器

3. FileInputFormat计数器

4. FileOutputFormat计数器

5. Job计数器

5.1.2 自定义计数器

1. 枚举类型定义计数器

2. 字符串类型定义计数器

5.2 连接

在关系型数据库中连接(join)是非常常见的操作。在海量数据的环境下,不可避免的也会遇到这种类型的需求。除了编写MapReduce程序,现在很多构建于Hadoop之上的应用,如Hive,PIG等在其内部都实现了join程序,用户可以通过很简单的sql语句或者数据操控脚本完成相应的join工作。

连接操作如果是由Mapper执行,则称为“map端连接”;如果由Reducer执行,则称为“reduce端连接”

六、MapReduce成绩分析系统解析

6.1 成绩分析系统解析

成绩管理系统应用非常广泛,但基本上都是基于关系型数据库进行实现。若现在已有学生各科成绩汇总的文本文件,如何对这些数据进行分析?创建数据库,将大量的数据手动添加到表中再通过SQL语句分析?显然这是不明智的选择。此时可以选择文本分析工具或MapReduce程序实现分析功能。

6.2 成绩分析系统功能设计

6.3 成绩分析系统实现

1. 学生的平均成绩

(1) 确定输入格式。采用默认的TextInputFormat。

(2) 确定输出格式。这里为了防止有重名的学生,输出平均成绩时同时输出学号和姓名。对于要输出三个值,可以使用字符串拼接的方式和自定义数据类型两种方法。这里采用自定义数据类型。

(3) 创建StudentWritable类,定义StudentWritable数据类型。

(4) 创建StudentMapper类,实现Mapper类。

(5) 创建StudentAverager类,实现主函数。

(6) 准备需要处理的数据。

(7) 运行。

2. 课程的最高成绩

(1) 确定输入格式。采用默认的TextInputFormat。

(2) 确定输出格式。输出课程名称与课程最高成绩,其中课程名称通过文件头部获取。

(3) 创建CourseMapper类,实现Mapper类。

(4) 创建CourseReducer类,实现Reducer类。

(5) 创建CourseMax类,实现主函数。

(6) 修改输出路径。

(7) 运行。

更多精彩内容请持续关注本站!

猜你喜欢

转载自blog.csdn.net/sinat_30844883/article/details/143251067