1.应用场景
二次排序是各类数据处理应用中经常遇到的问题,如学生成绩排名,先按照总分排名,如果总分一样按照英语成绩排名);海量日志分析中,日志先按照等级排名,FATAL(致命)等级的排在前面,ERROR等级次之,如果日志等级相同,则按照主机地址(IP)排名等。
我们先以简单的小学生成绩排名为例,来说明本案例的应用场景。排序规则为先按照语文成绩降序排列,如果语文成绩相同,则按照数学成绩降序排列。
输入
一班成绩:class1.txt
98 87
98 85
99 89
78 77
二班成绩:class2.txt
100 90
90 87
99 88
67 65
输出
100 90
99 89
99 88
98 87
98 85
90 87
78 77
67 65
2.实现分析
在进行MR编程时key键常用于分组或排序,但往往MR内置的key键数据类型并不能满足需求,需要自定义数据类型。
自定义类型分为四种:KEYIN、VALUEIN、KEYOUT、VALUEOUT
1.KEYIN:输入键的类型,应用广泛,利用MR的排序机制实现特殊目的的排序、分组等功能,一般用于Reducer的输入键类型;
2.VALUEIN:输入值类型,多用于Reducer的输入值类型;
3.KEYOUT:输出键的类型,应用较多,一般用于Mapper的输出键类型。
4.VALUEOUT:输出值类型,用于Mapper封装较复杂的数据结构供Reducer处理
IntWritable、LongWritable、FloatWritable、DoubleWritable等常数据类型都实现了WritableComparable接口。我们先来研究IntWritable的源码,了解MR中Key-Value的数据类型结构,通过解析源码也能规范自定义数据类型的实现。
IntWritable的源码主要构成部分:
实现了WritableComparable且必须使用泛型
@Public
@Stable
public class IntWritable implements WritableComparable<IntWritable> {
}
成员属性:private int value,维护了一个真实的int数据类型,即在MR机制中真实传输的值。
private int value;
构造方法:public IntWritable() {} 无参的构造器是必须的,否则MR在序列化的时候会出错,public IntWritable(int value)有参的构造器方便构造对象。
public IntWritable() {
}
public IntWritable(int value) {
this.set(value);
}
set、get:类似于POJO对象的setter和getter。
public void set(int value) {
this.value = value;
}
public int get() {
return this.value;
}
write(DataOutput out):将当前对象写出到输出流中,如果Mapper或Reducer指定的输出键值的类型为本对象类型,则在MR调用context.write时调用本方法。
public void write(DataOutput out) throws IOException {
out.writeInt(this.value);
}
readFields(DataInput in):将输入流解析成当前对象类型,如果Reducer的输入的键值类型为本对象,则由MR框架自动调用readFields方法。
public void readFields(DataInput in) throws IOException {
this.value = in.readInt();
}
equals和hashCode:用于判断两个对象是否相等。
public boolean equals(Object o) {
if(!(o instanceof IntWritable)) {
return false;
} else {
IntWritable other = (IntWritable)o;
return this.value == other.value;
}
}
public int hashCode()