一、二次排序发生在需要对 Key 排序的同时再对 Value 进行排序的情况下。
主要分为以下几个阶段:
Map 起始阶段
在Map阶段,使用 job.setInputFormatClass() 定义的 InputFormat ,将输入的数据集分割成小数据块 split,同时 InputFormat 提供一个 RecordReader的实现。本课程中使用的是 TextInputFormat,它提供的 RecordReader 会将文本的行号作为 Key,这一行的文本作为 Value。这就是自定义 Mapper 的输入是 < LongWritable,Text> 的原因。 然后调用自定义 Mapper 的map方法,将一个个< LongWritable,Text>键值对输入给 Mapper 的 map方法。
Map 最后阶段
在 Map 阶段的最后,会先调用 job.setPartitionerClass() 对这个 Mapper 的输出结果进行分区,每个分区映射到一个Reducer。每个分区内又调用 job.setSortComparatorClass() 设置的 Key 比较函数类排序。 可以看到,这本身就是一个二次排序。如果没有通过 job.setSortComparatorClass() 设置 Key 比较函数类,则使用 Key 实现的 compareTo() 方法。我们既可以使用 IntPair 实现的 compareTo() 方法,也可以专门定义 Key 比较函数类。
Reduce 阶段
在 Reduce 阶段,reduce() 方法接受所有映射到这个 Reduce 的 map 输出后,也是会调用 job.setSortComparatorClass()方法设置的 Key 比较函数类,对所有数据进行排序。然后开始构造一个 Key 对应的 Value 迭代器。 这时就要用到分组,使用 job.setGroupingComparatorClass()方法设置分组函数类。只要这个比较器比较的两个 Key 相同,它们就属于同一组,它们的 Value 放在一个 Value 迭代器,而这个迭代器的 Key 使用属于同一个组的所有Key的第一个Key。最后就是进入 Reducer 的 reduce() 方法,reduce() 方法的输入是所有的 Key 和它的 Value 迭代器,同样注意输入与输出的类型必须与自定义的 Reducer 中声明的一致。
二、具体流程:
1、自定义 key
所有自定义的 key 应该实现接口 WritableComparable,因为它是可序列化的并且可比较的。
//反序列化,从流中的二进制转换成IntPair
public void readFields(DataInput in) throws IOException
//序列化,将IntPair转化成使用流传送的二进制
public void write(DataOutput out)
//key的比较
public int compareTo(IntPair o)
//默认的分区类 HashPartitioner,使用此方法
public int hashCode()
//默认实现
public boolean equals(Object right)
2、自定义分区
自定义分区函数类 FirstPartitioner,是 key 的第一次比较,完成对所有 key 的排序。
public static class FirstPartitioner extends Partitioner< IntPair,IntWritable>
在 job 中使用 setPartitionerClasss()方法设置 Partitioner。
job.setPartitionerClasss(FirstPartitioner.Class);
3、Key 的比较类
这是 Key 的第二次比较,对所有的 Key 进行排序,即同时完成IntPair中的first和second排序。该类是一个比较器,可以通过两种方式实现。
1) 继承 WritableComparator。
public static class KeyComparator extends WritableComparator
必须有一个构造函数,并且重载 以下方法。
public int compare(WritableComparable w1, WritableComparable w2)
2) 实现接口 RawComparator。
上面两种实现方式,在 Job 中,可以通过setSortComparatorClass()方法来设置Key的比较类。
job.setSortComparatorClass(KeyComparator.Class);
如果没有使用自定义的 SortComparator 类,则默认使用 Key 中compareTo()方法对 Key 排序分组。
4、定义分组类函数
在 Reduce 阶段,构造一个与 Key 相对应的 Value 迭代器的时候,只要 first 相同就属于同一个组,放在一个 Value 迭代器。定义这个比较器,可以有两种方式。
1) 继承 WritableComparator。
public static class GroupingComparator extends