文章主要内容
承接文章:Hadoop之MapReduce
本文所有案例的代码:案例代码
在经过了上述过程之后,可以了解一下详细的Shuffle的工作流程。
3.2.1 Shuffle的前半部分
首先得到Key的hashcode,对numReducerTasks取模(得到的分区号就是0~numReducerTasks-1)
&一个Max_VALUE是防止hashcode出现负值
3.2.4 Partition分区案例实践
例3:分区实践
3.2.4.1 需求
将统计结果按照手机归属地不同省份输出到不同的文件中
期望输出:
手机号136、137、138、139开头分别放到一个独立的4个文件中,其他开头的放到一个文件中。
3.2.4.2 实现
这部分基于2.3的例1实现,把计算完流量的用户,按上述需求分为5个文件。
一个新的Partition–MyPartitioner:
public class MyPartitioner extends Partitioner {
public int getPartition(Text text, FlowBean flowBean, int numPartitions) {
String phone = text.toString();
switch (phone.substring(0,3)){
case "136":
return 0;
case"137":
return 1;
case "138":
return 2;
case "139":
return 3;
default:
return 4;
}
}
}
一个新的Driver-- MyPartitionDriver
public class MyPartitionDriver {
public static void main(String[] args) throws InterruptedException, IOException, ClassNotFoundException {
Job job = Job.getInstance(new Configuration());
job.setJarByClass(MyPartitionDriver.class);
job.setMapperClass(FlowMapper.class);
job.setReducerClass(FlowReducer.class);
//只新增了这两部分,设定一下Tasks的数量,和设定新写的Partition
job.setNumReduceTasks(5);
job.setPartitionerClass(MyPartitioner.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(FlowBean.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(FlowBean.class);
FileInputFormat.setInputPaths(job,new Path(args[0]));
FileOutputFormat.setOutputPath(job,new Path(args[1]));
boolean b = job.waitForCompletion(true);
System.exit(b?0:1);
}
}
3.2.5 shuffle机制
再回头看一下shuffle的机制
综上所述:分区号要从零开始逐一累加,一是防止报错,二是节约资源。
3.2.6 WritableComparable 排序
上述介绍中可以看出,排序是MapReduce框架中最重要的操作之一。
MapTask和ReduceTask均会对数据按照Key进行排序。该操作是Hadoop框架强制性的,任何应用程序中的数据均会被排序,不管逻辑上是否需要。
默认排序是按照字典顺序排序,实现排序的方法是快速排序。
例4:WritableComparable 排序案例
修改接口
接口要换成WritableComparable,其实这个接口就是Writable+Comparable,但是框架引用的是WritableComparable,所以分开来写是不行的,要写成一个接口。
重写排序方法
sum写成sun了,sunFlow就是总流量的意思。
SortMapper:
主要进行的就是赋值,并且把FlowBean当成Key值输出出去,这样框架就会自动对FlowBean进行排序。
public class SortMapper extends Mapper {
private FlowBean flow=new FlowBean();
private Text phone=new Text();
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
String[] field = value.toString().split("\t");
phone.set(field[0]);
flow.setUpFlow(Long.parseLong(field[1]));
flow.setDownFlow(Long.parseLong(field[2]));
flow.setSunFlow(Long.parseLong(field[3]));
context.write(flow,phone);
}
}
SortReducer:
就是把KV值转换一下,手机号变为Key,FLowBean变成Value。
public class SortReducer extends Reducer {
@Override
protected void reduce(FlowBean key, Iterable values, Context context) throws IOException, InterruptedException {
for (Text value : values) {
context.write(value,key);
}
}
}
SortDriver
跟之前的一致,注意一下调转Mapper的KV值的类型即可。
3.2.7 Combiner 合并
以wordCount为例:
在WcDriver中增加一句:
job.setCombinerClass(WcReducer.class); #因为在这里Combiner的作用和WcReducer一致
使用与未使用Combiner的差别:
3.2.8 GroupingComparator 分组(辅助排序)
对Reduce阶段的数据根据某一个或者几个字段进行分组
默认的分组规则是:两个KV对的Key相等就是一组的,不等就不是一组的。
例5:GroupingComparator分组案例实操
3.2.9 总结
总的来说,MapReduce的流程大致可以解释为:
输入->Mapper->分区&排序->Reducer->输出
一个分片一个MapTask,一个分区一个ReduceTask
实现代码的时候,只要按照这个顺序,写好输入输出和需求即可。
输入输出的关系:
逻辑需求:
Shuffle的流程总结:
简单来说:
Map方法出来的数据
具体细节:
环形缓冲区:
环形缓冲区的好处就是