二级排序
即对key和value双排序。默认情况下,Map输出的结果会对Key进行默认的排序,但是有时候需要对Key排序的同时还需要对Value进行排序,这时候就要用到二次排序了。
有两种方法进行二次排序,分别为:buffer and in memory sort和 value-to-key conversion。
1、buffer and in memory sort
在reduce()函数中,将某个key对应的所有value保存到内存中,然后进行排序。 这种方法最大的缺点是:可能会造成out of memory。
2、value-to-key conversion
MapReduce 程序中,Mapper 输出的键值对会经历 shuffle 过程再交给 Reducer。在 shuffle 阶段,Mapper 输出的键值对会经过 partition(分区)->sort(排序)->group(分组) 三个阶段。
既然 MapReduce 框架自带排序,那么二次排序中的排序是否可以交给 shuffle 中的 sort 来执行呢?
答案是肯定的,而且 shuffle 的 sort 在大量数据的情况下具有很高的效率。
将key和部分value拼接成一个组合key,这样reduce获取的结果便是先按key排序,后按value排序的结果,需要注意的是,用户需 要自己实现Paritioner,以便只按照key进行数据划分。Hadoop显式的支持二次排序,在Configuration类中有个 setGroupingComparatorClass()方法,可用于设置排序被group的key值。
shuffle 的 sort 过程会根据键值对<key, value>的 key 进行排序,但是二次排序中,value 也是需要排序的字段。因此需要将 value 字段合并到 key 中作为新的 key,形成新的键值对<key#value, value>。在排序时使其先根据 key 排序,如果相同,再根据 value 排序。
下图详细描述了二次排序的流程,虚线框内为 shuffle 过程:
可以发现,在 Map 阶段,程序将原本的 key 和 value 组合成一个新的 key,这是一个新的数据类型。因此需要在程序中定义该数据类型,为了使它支持排序,还需定义它的比较方式。根据二次排序的规则,比较方式为:先比较第一个字段,如果相同再比较第二个字段。
Map 操作结束后,数据的 key 已经改变,但是分区依旧要按照原本的 key 进行,否则原 key 不同的数据会被分到一起。举个例子,<1#1, 1>、<1#3, 3>、<1#5, 5>应该属于同一个分区,因为它们的原 key 都是1。如果按照新 key 进行分区,那么可能<1#1, 1>、<1#3, 3>在同一个分区,<1#5, 5>被分到另一个分区。
分区和排序结束后进行分组,很显然分组也是按照原 key 进行
參考: