在现实需求中,我们时长会遇到将执行结果按照某些条件存放到不同的结果文件中,Partition分区即可实现这个功能。
Partition分区有默认Partition分区和自定义Partition分区两种,下面我们逐一介绍。
一、默认Partition分区:
public class HashPartitioner<K, V> extends Partitioner<K, V> {
//提供空参构造器
public HashPartitioner() {
}
//默认Partition分区方法
public int getPartition(K key, V value, int numReduceTasks) {
return (key.hashCode() & 2147483647) % numReduceTasks;//2147483647为Integer的最大值,即为2的31次方-1.
}
}
默认分区源码如上,是根据key(键)的hashCode对ReduceTasks个数取模得到的。用户无法决定哪个key对应存储到哪个分区。程序默认执行该方法。默认的分区为1个,即结果文件就一个。
二、自定义Partition分区:
(1)自定义类继承Partition,重写getPartition()方法。
如下:
public class MyPartitioner extends Partitioner<Text, FlowBean>{
@override
public int getPartition(Text key, FlowBean value, int numpartitions){
//写自己的分区代码(示例如下):
// 1 获取序号的前三位
String id = key.toString().substring(0, 3);
int partition = 4;//定义partition默认值为4,即下面判断中未匹配到的项将全部放入4所对应的文件。
// 2 判断是哪个部门(只分了部门100、101、102、103和其它,其它就是上面对应的值为4的文件)
if ("100".equals(id)) {
partition = 0;//自定义分区的需要必须是从0开始,依次+1
}else if ("101".equals(id)) {
partition = 1;
}else if ("102".equals(id)) {
partition = 2;
}else if ("103".equals(id)) {
partition = 3;
}
//返回partition,对应到不同的结果文件。partition取值为0,1,2,3,4即需要产生5个结果文件
return partition;
}
}
(2)在Job驱动类中,设置自定义Partition类。
job.setPartitionerClass(MyPartitioner.class);
说明:普通自定义的MapReduce程序需要包含三个类:自定义的Map类、自定义的Reduce类、自定义的Driver类(即Job驱动类),将(2)(3)两个设置写到Job驱动类中即可。本文不详细讲解自定义MapReduce如何写。
(3)在Job驱动类中,根据自定义Partition类的结果文件个数设置相应数量的ReduceTask个数。实际产生结果文件的个数是由ReduceTask的数量决定的!
job.setNumReduceTasks(5);
说明:自定义Partition分区类中分区号必须从零开始,逐渐+1递增。
关于getPartition的结果文件个数和ReduceTask的个数:
(1)如果 getPartition的结果文件个数 = ReduceTask的个数 —>程序正常执行,按计划分区结果产生结果文件。
(2)如果 getPartition的结果文件个数 > ReduceTask的个数 —>会导致一部分分区数据无处存放,会Exception。
(3)如果 getPartition的结果文件个数 < ReduceTask的个数 —>会多产生几个空的结果文件Part-r-000xx。
特别的:如果ReduceTask的个数设置为1 —>则无论有多少个分区文件,最终结果只会产生一个结果文件,所有的结果都在该文件中。(ReduceTask的数量默认为1)。