Combiners和Partitioner编程
Combiners的作用:
每一个map可能会产生大量的输出,combiner的作用就是在map端对输出先做一次合并,以减少传输到reducer的数据量,
1)combiner最基本是实现本地key的聚合,对map输出的key排序,value进行迭代。如下所示:map: (K1, V1) → list(K2, V2) combine: (K2, list(V2)) → list(K2, V2) reduce: (K2, list(V2)) → list(K3, V3)
2)combiner还具有类似本地的reduce功能.例如hadoop自带的wordcount的例子和找出value的最大值的程序,combiner和reduce完全一致。如下所示:map: (K1, V1) → list(K2, V2) combine: (K2, list(V2)) → list(K3, V3) reduce: (K3, list(V3)) → list(K4, V4)
3)如果不用combiner,那么,所有的结果都是reduce完成,效率会相对低下。使用combiner,先完成的map会在本地聚合,提升速度。
4)对于hadoop自带的wordcount的例子,value就是一个叠加的数字,所以map一结束就可以进行reduce的value叠加,而不必要等到所有的map结束再去进行reduce的value叠加。
注意:combiner使用的合适,可以在满足业务的情况下提升job的速度,如果不合适,则将导致输出的结果不正确。
Combiner的输出是Reducer的输入,Combiner绝不能改变最终的计算结果。所以从我的想法来看,Combiner只应该用于那种Reduce的输入key/value与输出key/value类型完全一致,且不影响最终结果的场景。比如累加,最大值等。
Combiners分析
假设有两个map。
第一个map的输出为:
(1950,0)
(1950,20)
(1950,10)
第二个map输出为:
(1950,25)
(1950,15)
(1950,30)
Reduce函数被调用是,输入如下:
(1950,[0,20,10,25,15,30])
因为30是最大的值,所以输出如下:
(1950,30)
如果我们使用 combiner:那么reduce调用的时候传入的数据如下:
(1950,[20,30])--(1950,30)
用表达式表示为:
Max(0,20,10,25,15,30)=max(max(0,20,10),max(25,15,30))=max(20,30)=30
使用 Combiners要小心
刚才我们是计算最大值可以使用Combiners能提高效率。
如果我们要是求平均值呢?
Avg(0,20,10,25,15,30) = 15
如果使用Combiner会得到什么样的结果呢?
第一个map输出为:
avg(0,20,10) = 10
第二个map输出为:
Avg(25,15,30) = 23
输入到reduce出来的结果为:
Avg(10,23) = 17.5
17.5和15?
所以 :使用combiner一定要注意。
Partitioner分析
Partitioner 在mapreduce的位置:
Partition主要作用就是将map的结果发送到相应的reduce。这就对partition有两个要求:
1)均衡负载,尽量的将工作均匀的分配给不同的reduce。
2)效率,分配速度一定要快。
1:Partitioner是partitioner的基类,如果需要定制partitioner也需要继承该类。
2:HashPartitioner是mapreduce的默认partitioner。计算方法是
which reducer=(key.hashCode() & Integer.MAX_VALUE) % numReduceTasks,得到当前的目的reducer。
3:BinaryPatitioner继承于Partitioner< BinaryComparable ,V>,是Partitioner的偏特化子类。该类提供leftOffset和rightOffset,在计算which reducer时仅对键值K的[rightOffset,leftOffset]这个区间取hash。
Which reducer=(hash & Integer.MAX_VALUE) % numReduceTasks
4:KeyFieldBasedPartitioner也是基于hash的个partitioner。和BinaryPatitioner不同,它提供了多个区间用于计算hash。当区间数为0时KeyFieldBasedPartitioner退化成HashPartitioner。
5.TotalOrderPartitioner这个类可以实现输出的全排序。不同于以上3个partitioner,这个类并不是基于hash的。在下一节里详细的介绍totalorderpartitioner。
自定义的Partitioner
1)为何使用Partitioner,主要是想reduce的结果能够根据key再次分类输出到不同的文件夹中。
2)结果能够直观,同时做到对数据结果的简单的统计分析。
需求:
1、输入的数据文件内容如下(1条数据内容少,1条数据内容超长,3条数据内容正常):
Kaka 1 28
hua 0 26
chao 1
tao 1 22
mao 0 29 22
2、目的是为了分别输出结果,正确的结果输出到一个文本,太短的数据输出到一个文本,太长的输出到一个文本,共三个文本输出。
Patitioner接口:
public int getPartition(Text key, Text value, int numPartitions);
numPartitions为Reduce的个数。
注:在本地作业运行器上运行时,只支持0个或一个Reduce。