分区
分区与reducetask的并行度有关
reducetask:运行reduce类的任务,成为reducetask
reducetask的并行度是指一个job中reducetask运行的个数
默认的reducetask的个数:
默认值是1,reducetask只执行一个
< property >
< name>mapreduce.job.jvm.numtasks</ name>
< value>1< /value>
< /description>
< /property>
如何调整这个参数:
在代码中修改
//设置reducetask的个数,参数表示reducetask的个数
job.setNumReduceTasks(4);
表象表现
输出结果文件和reducetask的个数一一对应
reducetask的编号从0开始, 顺序递增
part-r-00000 00000表示的是reducetask的编号
part-r-00001 reducetask编号为1的输出
part-r-00002 reducetask编号为2的输出
part-r-00003 reducetask编号为3的输出
观察输出结果:发现4个结果文件合起来就是原来的一个输出结果的文件
每一个reducetask的数据的分配是由分区算法决定的
分区;map输出的数据被按照一定的规则分成了多个部分,每一部分称为一个分区
默认情况下的分区数量等于reducetask的个数
默认的分区算法;map输出的key.hash%reducetask的个数
默认的分区的底层实现
public abstract class Partitioner<KEY, VALUE> {
//返回值int 返回值代表的是分区编号 分区编号从0开始顺序递增的
/*
KEY key map输出的key
VALUE value map 输出的value
int numPartitions 分区的数量
*/
public abstract int getPartition(KEY key, VALUE value, int numPartitions);
}
默认的实现类是:HashParttiioner
public class HashPartitioner<K, V> extends Partitioner<K, V> {
/*
默认的分区算法
默认的分区个数===reducetask的个数
分区编号是个非负数
& Integer.MAX_VALUE 取绝对值
*/
public int getPartition(K key, V value,
int numReduceTasks) {
//分区算法
return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
}
}
分区算法决定的是maptask输出的数据如何分配给reducetask的
自定义分区
1)继承Partitioner
2)重写getPartition()方法
3)在job中指定分区类
//指定分区类
job.setPartitionerClass(myPartition.class);
4)指定reducetask的个数
//指定reducetask的个数
job.setNumReduceTasks(4);
案例:
自定义分区
package com.lee.partition;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Partitioner;
/*
* 自定义分区
泛型指的是 map输出的key,value的类型
* */
public class myPartition extends Partitioner<Text, Text>{
//分区方法
@Override
public int getPartition(Text key, Text value, int numPartitions) {
//按手机号分区
/*
* 134-136 北
* 137-139 上
* 150 广
* 剩余 深
*
* */
String k = key.toString();
String prefix = k.substring(0,3);
if("134".compareTo(prefix)<0 && "136".compareTo(prefix)>=0) {
//返回分区编号
return 0;
}else if("137".compareTo(prefix)<0 && "139".compareTo(prefix)>=0){
return 1;
}else if("150".equals(prefix) ){
return 2;
}else {
return 3;
}
}
}
MapReduce代码
package com.lee.partition;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
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;
public class FlowPartition {
/*
* 分区只会按照 map输出的key,分区字段放在map输出的key中
*
* map:
* key:手机号
* value:其他
*
* shuffle ---分区---reducetask
* reduce:
* 相同的手机号的数据接收一次
* 直接输出
*
* */
static class myMapper extends Mapper<LongWritable, Text, Text, Text>{
Text k=new Text();
Text v=new Text();
//一行调用一次
@Override
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, Text>.Context context)
throws IOException, InterruptedException {
String[] datas = value.toString().split("\t");
//封装k,v
k.set(datas[0]);
v.set(datas[1]+"\t"+datas[2]+"\t"+datas[3]);
context.write(k, value);
}
}
static class myReduce extends Reducer<Text, Text, Text, Text>{
/*
* 分区 排序 分组
*
* 1、分区,按照自定义的规则
* 2在每一个分区中 先排序,再分组
*
* 每一个分区的每一组数据
* 相同的手机号会分为一组
* */
@Override
protected void reduce(Text key, Iterable<Text> values, Context context)
throws IOException, InterruptedException {
//直接输出就可以了
for (Text v : values) {
context.write(key, v);
}
}
}
public static void main(String[] args) throws IllegalArgumentException, IOException, ClassNotFoundException, InterruptedException {
Configuration conf = new Configuration();
Job job=Job.getInstance(conf);
job.setJarByClass(FlowPartition.class);
job.setMapperClass(myMapper.class);
job.setReducerClass(myReduce.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
//指定分区类
job.setPartitionerClass(myPartition.class);
//指定reducetask的个数
job.setNumReduceTasks(4);
FileInputFormat.addInputPath(job, new Path("E:\\flowout_02"));
//只要有reduce类,就会有输出路径
//输出路径的作用:存放输出标志文件_success
FileOutputFormat.setOutputPath(job, new Path("e:\\flowout_partition1"));
boolean waitForCompletion = job.waitForCompletion(true);
System.exit(waitForCompletion?0:1);
}
}
结果显示