了解MapReduce之Partition分区的概念与执行过程(附例子)

Partition

1.用户需求

             在执行MR程序时我们可能想要将不同的数据放到不同的文件中。

2.概念

             partition是在map阶段完成后执行的。然后将分好区的数据传输到reduce端,也就是由Partitioner来决定每条记录应该送往哪个reducer节点。mapreduce中默认的分区是HashPartition类;
           核心代码:
      
    
public class HashPartitioner<K, V> extends Partitioner<K, V> {

          /** Use {@link Object#hashCode()} to partition. */
          public int getPartition(K key, V value,
                          int numReduceTasks) {
            return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
          }

}
上面的getPartition函数的作用:
             1)、获取key的哈希值
             2)、使用key的哈希值对reduce任务数求模
             3)、这样做的目的是可以把(key,value)对均匀的分发到各个对应编号的reduce task节点上,达到reduce                     task节点的负载均衡。

3.自定义分区

                 自定义分区很简单,我们只需要继承抽象类Partitioner重写getPartition方法即可,
         另外还要给任务设置分区:  job.setPartitionerClass(),就可以了。
         注意:
                    自定义分区的数量需要和reduce task的数量保持一致。

4.例如:

public class MyPartition extends Partitioner<LongWritable,Text>{

	@Override
	public int getPartition(LongWritable key, Text value, int numPartitions) {
                                //必须接收来自Mapper的<k2,v2>的类型
		if(key.get()%2 ==0){
			 key.set(2);
			 return 0;
		}else{
			key.set(1);
			return 1;
		}
	}	
}

5.应用

    将上一个博客的输出内容奇偶行分别求和的结果分别输出到两个文件中
即:  奇数行和为:55       一个文件分为两个文件输出:文件1:奇数行和为:55
         偶数行和为:155                                                 文件2:偶数行和为:155 

  (附上一个博客地址:https://blog.csdn.net/xiaozelulu/article/details/80965576

需要修改的地方(在上一个博客的基础上修改):

        1)Mapper阶段:       

protected void map(LongWritable key, Text value,
			org.apache.hadoop.mapreduce.Mapper.Context context)
			throws IOException, InterruptedException {
		 //System.out.println("before Mapper  <k1,v1>:"+key+"-"+value);//1 10 		 
	        /* if(key.get()%2==0){ //key是行号                           
			context.write(new LongWritable(2l), value);//将2或4写进去,作为是key
			System.out.println("After  Mapper偶数<k2,v2>:"+new LongWritable(2l)+"-"+value); //1 10
		}else{
			context.write(new LongWritable(1l), value);//此时偏移量都给了一个固定的值
			System.out.println("After  Mapper奇数<k2,v2>:"+new LongWritable(1l)+"-"+value); //1 10
		}*///Mapper不做处理,此时已经分区partition了
		 context.write(key, value);//只需要该行,其他注释掉
	}
}	

    2)新建一个MyPartition.class类

public class MyPartition extends Partitioner<LongWritable,Text>{//

	@Override
	public int getPartition(LongWritable key, Text value, int numPartitions) {//必须接收来自Mapper的类型
		if(key.get()%2 ==0){
			 key.set(2);
			 return 0;
		}else{
			key.set(1);
			return 1;
		}
	}		
}

3)在执行主类中添加

          job.setPartitionerClass(MyPartition.class);//加载自定义分区类
         job.setNumReduceTasks(2);//因为此时我们有两个reducer输出文件,
                                                        part-r-00000和 part-r-00001,默认reducer文件为1。
            

4)结果

                输出两个文件,如下
                part-r-00000文件: 奇数行和为:55        
                part-r-00001文件: 偶数行和为:155        

6.总结

                map输出后执行partition阶段,此时已经分好区了(假如分区为:part-0,part-1),
        然后执行reducer阶段-->>判断map输出的数据哪些是满足part-0的条件还是part-1;
        最后将需要输出的数据输出到两个输出文件就可以了。


附:
【partition分区:
             a.  如果reduceTask的数量> getPartition的结果数,则会多产生几个空的输出文件part-r-000xx;
             b. 如果1<reduceTask的数量<getPartition的结果数,则有一部分分区数据无处安放,会Exception;
         c. 如果reduceTask的数量=1,则不管mapTask端输出多少个分区文件,最终结果都交给这一个reduceTask,( 最终也就只会产生一个结果文件 part-r-00000;但是分区part-1的结果不知道要不要放进
part-0文件中,最终会产生紊乱;)

        紊乱的文件part-r-00000:如下
        
奇数行和为:	1
偶数行和为:	20
奇数行和为:	2
奇数行和为:	11
奇数行和为:	3
奇数行和为:	12
奇数行和为:	4
奇数行和为:	13
奇数行和为:	5
奇数行和为:	14
奇数行和为:	6
奇数行和为:	15
奇数行和为:	7
奇数行和为:	16
奇数行和为:	8
奇数行和为:	17
奇数行和为:	9
奇数行和为:	18
奇数行和为:	10
奇数行和为:	19








猜你喜欢

转载自blog.csdn.net/xiaozelulu/article/details/80975899