5.5 分区(Partitioner)
分区(Partitioner
)的作用是将所有的数据输出到同一个out文件中,分区可以将数据输出到不同的文件中。因为Reduce任务最终的输出就是整个job的输出,因此分区是针对于Reduce任务的数量来说的。
分区的作用:
例如一个网站允许同时邮箱和手机号码注册,那么我们现在要求将用户名是邮箱和手机号的账号信息分别输出到一个文件中,此时就要用到分区,邮箱号的账户一个属于同一个分区,手机号码的账号属于同一个分区。也就是说,分区的作用是将分区后的数据进行分门别类的保存。
设置分区的代码如下:
//设置reducer任务数,默认为1 job.setNumReduceTasks(2); //设置分区类 job.setPartitionerClass(HashPartitioner.class);
HashPartitioner的源码如下:
public class HashPartitioner<K, V> extends Partitioner<K, V> { /**reduce任务输出的value *reduce任务输出的value *numReduceTasks 分区的数量,也就是reduce任务的数量 */ public int getPartition(K key, V value,int numReduceTasks) { return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks; } }
源码分析:
(key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
HashPartitioner
是根据reduce任务输出的key值进行hash分区,使用key.hashCode()取得hash值。我们知道在java中,每个hashcode的值都基本上是唯一的,但是其值是int型,也就是说取值范围是-231到231-1之间。而整数最大值二进制表示为0x7fffffff,当进行了与操作之后,只有最高位会改变为0,其他位的数值都是不变的,这样就可以把负数的hashcode值转化为正数。
另外我们知道一个正数对另外一个正数n取模,那么结果范围肯定是0...n-1。
假设我们现在设置的numReduceTasks为2,那么经过取模后,结果就是0,1。也就是说我们输出的分区,一个编号是0,一个编号是1。那么hadoop就会自动帮我们创建2个输出文件,一个输出文件编号为0,一个文件输出编号为1.然后针对key去hashcode取模,将结果输出到相应的文件中。
例如,如果我们经过了上述设置,那么对于WordCount应用,运行结果的输出应该为
总结:
对于HashPartitioner,其是根据key的hashcode,对numReduceTasks取模,因此我们有多少个numReduceTasks,就会有多少个输出分区。即reduce任务的数量等于分区的数量。
但是要注意的是,reduce任务的数量和输出分区的数量没有必然的关系。针对我们自己的WordCount,我们完全可以自定义一个分区类,不使用key进行分区,而是根据value进行分区。不管numReduceTasks设置为多少,我们value为1的输出到一个分区,value不为1的输出到另一个分区。