mapreduce 自定义分区的实现

题目:

有如下流量数据,要求按照手机号归属地进行分区。
其中:
134—136 归属地属于上海
137—138 归属地属于北京
139—159 归属地属于深圳
其它 归属地未知

手机号			上行流量		下行流量		总流量
13480253104		2494800		2494800		4989600
13502468823		101663100	1529437140	1631100240
13560436666		15467760	13222440	28690200
13560439658		28191240	81663120	109854360
13602846565		26860680	40332600	67193280
13660577991		96465600	9563400		106029000
13719199419		3326400		0			3326400
13726230503		34386660	342078660	376465320
13726238888		34386660	342078660	376465320
13760778710		1663200		1663200		3326400
13826544101		3659040		0			3659040
13922314466		41690880	51559200	93250080
13925057413		153263880	668647980	821911860
13926251106		3326400		0			3326400
13926435656		1829520		20956320	22785840
15013685858		50713740	49036680	99750420
15920133257		43742160	40692960	84435120
15989002119		26860680	2494800		29355480
18211575961		21164220	29189160	50353380
18320173382		132099660	33430320	165529980
84138413		57047760	19847520	76895280
分析:

此题,默认的分区算法,无法实现,需要进行自定义分区
自定义分区流程如下:

1)定义一个类  继承  Partitioner
2)重写 getPartition
3)在job中,指定自定义分区类
	job.setPartitionerClass(cls); 
4)指定reducetask的个数  不指定默认值运行一个
	job.setNumReduceTasks(4);
程序框架如下:

FlowPartition类实现mr过程
MyPartitioner类实现自定义分区

MyPartitioner类的代码实现如下:
package com.aura.cn.partition;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Partitioner;
/**
*@class MyPartitioner.java
*@author Samuel 
*@version v1.0
*@date 2019年12月16日上午10:49:30
*@description    自定义分区
*按照手机号进行分区
*
 * 分区类
 * 按照手机号分区
 * 		134---136   上海  part-r-00000   0
		137---138   北京  part-r-00001   1
		139---159   深圳  part-r-00002   2
		其他    	未名  part-r-00003   3
		4 个reducetasks -- 4个分区
	1)继承  partitioner
	2)getPartitioner
	
	泛型:map输出的k  v

*/
public class MyPartitioner extends Partitioner<Text, Text>{
	/*
	 * numPartitions 默认情况下,等于 reducetask 的个数
	 */
	@Override
	public int getPartition(Text key, Text value, int numPartitions) {
		String prefix = key.toString().substring(0,3);
//		int prefix1 = Integer.parseInt(prefix);
//		if(prefix1>=134 && prefix1<=136) {
//			用此种方式也可以。
//		}
		if(prefix.compareTo("134")>=0 && prefix.compareTo("136")<=0) {
			return 0;
		}else if(prefix.compareTo("137")>=0 && prefix.compareTo("138")<=0){
			return 1;
		}else if(prefix.compareTo("139")>=0 && prefix.compareTo("159")<=0){
			return 2;
		}else {
			return 3;
		}
	}
}
FlowPartition类的代码实现如下:
package com.aura.cn.partition;

import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

/**
*@class FlowPartition.java
*@author Samuel 
*@version v1.0
*@date 2019年12月16日上午10:21:04
*@description   实现mr过程
*/
public class FlowPartition {
	/*
	 * 	
	 *	map:
			key:手机号
			value:Text (其他内容)
		shuffle:
			自定义分区
			按照手机号归属地  分区
			排序  分组
		一行内容为:13480253104	2494800	2494800	4989600
	 */

	static class PartitionMapper extends Mapper<LongWritable, Text, Text, Text>{
		Text mk = new Text();
		Text mv = new Text();
		@Override
		protected void map(LongWritable key, Text value, Context context)
				throws IOException, InterruptedException {
			String line = value.toString();
			String[] datas = line.split("\t");
			mk.set(datas[0]);
			String subValue = line.substring(line.indexOf(datas[1]));//获取value的值
			mv.set(subValue);
			context.write(mk, mv);//发送
		}
	}
	
	/*
	 * shuffle过程:
	 * 	1)分区  map key前3位
	 * 134---136   
	 * 137---138
	 * 139---159
	 **  未知
	 * 
	 * 2)排序  map key
	 * 每一个分区内部  分别排序
	 *	13480253104	2494800		2494800		4989600
		13502468823	101663100	1529437140	1631100240
		13560436666	15467760	13222440	28690200
		13560439658	28191240	81663120	109854360
		13602846565	26860680	40332600	67193280
		13660577991	96465600	9563400		106029000
	3)分组  map key
		相同手机号分在一组
		
	reduce端:
		shuffle已经完成对每一个分区的数据进行排序、分组 
		所以reduce端直接输出即可
	 */
	
	
	// 每一个reducetask, 都会实例化PartitionReducer。  每一个对象中, 反复调用reduce方法。
	//PartitionReducer 这个类,针对的是每一个分区。
	static class PartitionReducer extends Reducer<Text, Text, Text, NullWritable>{
		Text rk = new Text();
		@Override
		protected void reduce(Text key, Iterable<Text> values,Context context)
				throws IOException, InterruptedException {
			//循环遍历输出
			for (Text v : values) {
				rk.set(key.toString()+"\t"+v.toString());
				context.write(rk, NullWritable.get());
			}
		}
	}
	
	public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
		Configuration conf = new Configuration(); //获取集群配置
		Job job = Job.getInstance();//构建job对象
		job.setJarByClass(FlowPartition.class);//指定jar包运行主类
		job.setMapperClass(PartitionMapper.class);//指定map类
		job.setReducerClass(PartitionReducer.class);//指定reduce类
		job.setMapOutputKeyClass(Text.class); //指定map输出的 key的类
		job.setMapOutputValueClass(Text.class);//指定map输出的 value的类
		job.setOutputKeyClass(Text.class);//指定reduce输出的 key的类
		job.setOutputValueClass(NullWritable.class);//指定reduce输出的 value的类
		
		//指定分区类
		job.setPartitionerClass(MyPartitioner.class);
		//指定reducetask的个数
		job.setNumReduceTasks(4);
		
		FileInputFormat.addInputPath(job, new Path("D:\\bd1904\\data\\{flowout/*}") ); //指定输入路径
		
		FileSystem fs = FileSystem.get(conf); 
		Path outPath =new Path("D:\\bd1904\\data\\flowout_partition");
		if(fs.exists(outPath)) {
			fs.delete(outPath, true);
		}
		FileOutputFormat.setOutputPath(job, outPath); //指定输出路径
		job.waitForCompletion(true);//提交, 打印日志
	}
}
总结:

注意:
1)自定义分区中,分区和reducetask是一 一对应的, 因此,分区编号一定要和reducetask的编号一 一对应。
2)reducetask的编号,默认从0开始,顺序递增的。

虽然自定义分区中,分区编号是可以自己定义返回值的,不一定要顺序递增。但是出于性能考虑,分区编号最好是顺序递增的,reducetask的设置和分区个数相同,否则必然有reducetask在执行空跑。
reducetask设置为 1 或者大于返回值最大值,程序可以跑通。如果reducetask设置的值不大于自定义返回值的最大值,则会报 Illegal partition for … 的错误。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值