《Data-intensive Text Processing with MapReduce》读书笔记第3章:MapReduce算法设计(4)
3.4 二次排序
在中间结果进入reducer之前,MapReduce会先将这些中间结果进行排序,然后再进行分发。这个机制对于依赖中间结果输入顺序(按照key的顺序)的reduce操作非常方便。反序(order inversion)模式便是利用这个机制的一个例子。但如果有更进一步的排序需求呢(在前面的基础上再按value排序)?有了二次排序,能够保证到达reducer的数据不仅是key有序,同时也是value有序的。Google的MapReduce实现内置对二次排序的支持(可选,也就是说,二次排序并不是必须进行的操作),而Hadoop非常不幸(甚至可以说是悲催)没有内置对二次排序的支持,因此有了这个“值键转换(value-to-key conversion)”模式。
考虑一个传感器数据的例子:现有m(m很大)个传感器,对这m个传感器连续读数(这样一来每次便读到m组数据)。如此得到如下数据:
其中tx是时间点,mx是传感器编号,rx是传感器读数(rx具体内容在此不关注)。
假如现在我们希望重现各个传感器的工作状态,这样就需要根据传感器划分数据。现在我们设计一个MapReduce算法,其中的mapper读入原始数据,输出如下结构的数据(以上面原始数据的第一条为例说明):
(m1,(t1,r80521))
显然来自于同一个传感器的key-value对将会进入相同的reducer,且MapReduce能够保证输入reducer的key-value对是按照key(也就是mx)有序的。但MapReduce无法保证对于每组具有相同mx值的key-value对中,tx也是有序的(因为并没有按照tx排过序)。解决这个问题最直观、最简单的方法就是将每组数据(即形如(mi, [(ti1,ri1),(ti2,ri2),...(tin,rin)])的reducer输入数据)读入内存,然后按照tx进行排序。但这种方法受到内存容量的限制,可扩展性不佳。
二次排序与键值转换
要进行的操作实际上就是二次排序。对于二次排序的需求在很多应用中都存在:先按照一个键(本例中是mx)对全部数据进行排序,然后对于含有相同键值的每一小段再按照另一个键排序(本例中是tx)。虽然Hadoop没有提供原生的二次排序支持,但幸运地是,二次排序可以通过应用“值键转换(value-to-key conversion)”模式折中进行。键值转换的基本思路是将值(value)中需要排序的键移至键(key)中。对于以上的例子,可以改变mapper输出的key-value对格式,使其变成:
((m1,t1),r80521)
剩下的工作就很简单了:
1. 自定义排序:先按mx排,再对每组按tx排
2. 自定义partitioner:按照key中的mx划分
使用键值转换,不仅可以实现两个键上的排序,还可以实现更多键上的排序。