MapReduce原理——Shuffle机制
在Map方法之后,Reduce方法之前的数据处理过程称之为Shuffle.
Map方法输出的数据会获得对应的分区,进入环形缓冲区(缓冲区一半写索引,另一半写数据)。数据达到缓冲区的80%会发生溢写。在溢写之前会对key索引进行快排(按照数据字典),最后对分区进行归并排序。在归并后还可进行对数据的压缩,帮助将数据写入磁盘中。
Partition分区
要求将统计结果按照条件输出到不同的文件中(分区)。比如手机号按照归属地不同身份输出到不同文件中(分区)
源码分析
以wordCount
在driver中添加代码
instance.setNumReduceTasks(2);
在mapper中的context.write()方法打断点
进入最后的write()方法里,collector就是环形缓冲区,然后进去参数里的方法
进入获得分区的方法 getPartition()
public int getPartition(K key, V value, int numReduceTasks) {
return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
}
这个方法是设置的默认分区,根据key的hashCode对ReduceTask个数取模得到的,用户没法控制那个key存储到分区中。
自定义Partitioner步骤:
定义类继承Partitioner,重写getPartitioner()方法
在job驱动中设置定义的partitioner.
设置reducetask的数量。
自定义设置分区案例
package com.rsh.mapreduce.partitioner2; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Partitioner; public class ProvincePartitioner extends Partitioner<Text,FlowBean>{ @Override public int getPartition(Text text, FlowBean flowBean, int numPartitions) { int partition; String phone = text.toString(); String prePhone = phone.substring(0, 3); if("136".equals(prePhone)){ partition = 0; } else if ("137".equals(prePhone)) { partition = 1; }else if ("138".equals(prePhone)) { partition = 2; }else if ("139".equals(prePhone)) { partition = 3; }else { partition = 4; } return partition; } }
package com.rsh.mapreduce.partitioner2; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; import java.io.IOException; public class FlowDriver { public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException { //获取job对象 Configuration configuration = new Configuration(); Job job = Job.getInstance(configuration); //关联本driver类 job.setJarByClass(FlowDriver.class); //关联Mapper、Reducer类 job.setMapperClass(FlowMapper.class); job.setReducerClass(FlowReducer.class); //设置Map的outKV类型 job.setOutputKeyClass(Text.class); job.setOutputValueClass(FlowBean.class); //设置程序最终输出类型 job.setOutputKeyClass(Text.class); job.setOutputValueClass(FlowBean.class); job.setPartitionerClass(ProvincePartitioner.class); job.setNumReduceTasks(5); //设置程序的输入输出路径 FileInputFormat.setInputPaths(job,new Path("D:\\hadoopMR\\MRInput\\flow.txt")); FileOutputFormat.setOutputPath(job,new Path("D:\\hadoopMR\\MROutput5")); //提交job boolean b = job.waitForCompletion(true); System.exit(b ? 0 : 1); } }