(转载)Hadoop常用SDK系列五 TotalOrderPartitioner

在0.19.0以前的版本中,Hadoop自身并没有提供全排序的solution,如果使用缺省的partitioner(HashPartitioner)每个reducer的输出自身是有序的,但是多个reducer的输出文件之间不存在全序的关系;如果想实现全排序,需要自己实现Partitioner,比如针对key为Mac地址的Partitioner,如假定Mac地址的分布是均匀的,可以根据Mac地址的前两个字节构造不超过255个reducer的Partitioner;但是这种Partitoiner是应用逻辑相关的,因此没有通用性,为此Hadoop 0.19.0提供了一个通用的全序Partitioner。

TotalOrderPartitioner最初用于Hadoop Terasort,也许是考虑到其通用性,后来作为0.19.0的release feature发布。

Partitioner的目的是决定每一个Map输出的Record由哪个Reducer来处理,它必须尽可能满足
1. 平均分布。即每个Reducer处理的Record数量应该尽可能相等。

2. 高效。由于每个Record在Map Reduce过程中都需要由Partitioner分配,它的效率至关重要,需要使用高效的算法实现。
获取数据的分布

对于第一点,由于TotalOrderPartitioner事先并不知道key的分布,因此需要通过少量数据sample估算key的分布,然后根据分布构造针对的Partition模型。

0.19.0中有一个InputSampler就是做这个事情的,
通过指定Reducer个数,并读取一部分的输入数据作为sample,将sample数据排序并根据Reducer个数等分后,得到每个Reducer处理的区间。比如包含9条数据的sample,其排好序的key分别为:
a b c d e f g h i
如果指定Reducer个数为3,每个Reducer对应的区间为

Reducer0 [a, b, c]
Reducer1 [d, e, f]
Reducer2 [g, h, i]

区间之间的边界称为Cut Point,上面三个Reducer的Cut point为 d, gInputSampler将这cut points排序并写入HDFS文件,这个文件即包含了输入数据的分布规律。

根据分布构建高效Partition模型

对于上面提到的第2点,高效性,
在读取数据的分布规律文件之后,TotalOrderPartitioner会判断key是不是BinaryComparable类型的。

BinaryComparable的含义是“字节可比的”,o.a.h.io.Text就是一个这样的类型,因为两个Text对象可以按字节比较,如果对应的字节不相等就立刻可以判断两个Text的大小。

先说不是
BinaryComparable类型的情况,这时TotalOrderPartitioner会使用二分查找BinarySearch来确定key属于哪个区间,进而确定属于哪个Reducer,每一次查找的时间复杂度为O(logR),R为Reducer的个数。

如果key是
BinaryComparable类型,TotalOrderPartitioner会根据cut points构造TrieTrie是一种更为高效的用于查找的数据结构,这种数据结构适合key为字符串类型,如下图

TotalOrderPartitioner中的Trie缺省深度为2,即使用2+1个prefix构造Trie;每个父节点有255个子节点,对应255个ASCII字符。查找的时间复杂度为O(m),m为树的深度,空间复杂度为O(255m-1),可以看到,这是一种空间换时间的方案,当树深度为2时,可以最多分配255 * 255个reducer,这在绝大情况下足够了。

可以看到,使用
Trie进行Partition的效率高于binarySearch,单次执行两种查找可能不会有什么感觉,但是当处理亿计的Record时,他们的差距就明显了。
 
posted on 2008-12-06 12:47  彭帅  阅读(3024)  评论(0编辑  收藏  举报