7月31日
Shuffle 机制
Map 方法之后,Reduce 方法之前的数据处理过程称之为 Shuffle。
我自己理解成一个将数据清洗整理的过程 ,用快排 通过索引就是key 按字典顺序来排序
以下都是在shuffle中的操作:
Partition分区
求将结果按照条件输出到不同文件中(分区),这一步在流程上是在map的输出过程中执行的

在此处打断点,进入源码来探究
自定义设置分区
第一步 自定义分区类 按要求自定义
例如:我按电话号码前三位来进行分区
public class ProvincePartitioner extends Partitioner<Text, FlowBean> {
@Override
public int getPartition(Text text, FlowBean flowBean, int i) {
String phone = text.toString();
String prePhone = phone.substring(0, 3);//包左边不包右
//定义一个分区号变量 partition,根据 prePhone 设置分区号
int partition;
if("152".equals(prePhone)){
partition = 0;
}else if("183".equals(prePhone)){
partition = 1;
}else if("137".equals(prePhone)){
partition = 2;
}else {
partition = 3;
}
//最后返回分区号 partition
return partition;
}
}
第二步
在driver设置分区类
job.setPartitionerClass(ProvincePartitioner.class);
第三步
在driver设置ReduceTask数量
job.setNumReduceTasks(4);
分区注意点:
(1) 如果ReduceTask的数量> getPartition的结果数,则会多产生几个空的输出文件part-r-000xx;
(2) 如果1<ReduceTask的数量<getPartition的结果数,则有一部分分区数据无处安放,会Exception;
(3) 如 果ReduceTask的数量=1,则不管MapTask端输出多少个分区文件,最终结果都交给这一个 ReduceTask,最终也就只会产生一个结果文件 part-r-00000;
(4) 分区号必须从零开始,逐一累加。
做自定义排序
Map结束后reduce之前会有一个排序 ,一般是默认排序
可以根据自己需要手动排序
例如:序列化案例产生的结果再次对总流量进行倒序排序。
这里就在前处理好的结果 文件当作输入文件(该文件已经没有重复手机号码)
在flowBean中增加排序内容,因为bean要做key值来进行操作,需要比较和序列化接口
注意比较方法的写法,和mapper reducer的泛型
public class FlowBean implements WritableComparable<FlowBean> {
private long upFlow;//上行流量
private long downFlow;//下行流量
private long sumFlow;//总流量
//空参构造
public FlowBean() {
}
public long getUpFlow() {
return upFlow;
}
public void setUpFlow(long upFlow) {
this.upFlow = upFlow;
}
public long getDownFlow() {
return downFlow;
}
public void setDownFlow(long downFlow) {
this.downFlow = downFlow;
}
public long getSumFlow() {
return sumFlow;
}
public void setSumFlow(long sumFlow) {
this.sumFlow = sumFlow;
}
public void setSumFlow() {
this.sumFlow = this.downFlow+this.upFlow;
}
@Override
public void write(DataOutput out) throws IOException {
out.writeLong(upFlow);
out.writeLong(downFlow);
out.writeLong(sumFlow);
}
@Override
public void readFields(DataInput in) throws IOException {
this.upFlow=in.readLong();
this.downFlow=in.readLong();
this.sumFlow=in.readLong();
}
@Override
public String toString() {
return upFlow +"\t" + downFlow + "\t" + sumFlow ;
}
@Override
public int compareTo(FlowBean o) {
//按照总流量的倒序进行排序
if(this.sumFlow > o.sumFlow){ //我理解this相当于形参
return -1;
}else if(this.sumFlow < o.sumFlow){
return 1;
}else {
//在之前排序条件下按上行流量正序排
if(this.upFlow>o.upFlow){
return 1;
}else if (this.upFlow<o.upFlow){
return -1;
}else {
return 0;
}
}
}
}
修改mapper 和reducer
public class FlowMapper extends Mapper<LongWritable, Text,FlowBean,Text> {
private Text outV=new Text();
private FlowBean outK=new FlowBean();
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
//获取一行
String line = value.toString();
//切割
String[] split = line.split("\t");
//封装
outK.setUpFlow(Long.parseLong(split[1]));
outK.setDownFlow(Long.parseLong(split[2]));
outK.setSumFlow();
outV.set(split[0]);
// 写出 outK outV
context.write(outK,outV);
}
}
public class FlowReducer extends Reducer<FlowBean,Text, Text, FlowBean> {
@Override
protected void reduce(FlowBean key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
//遍历 values 集合,循环写出,避免总流量相同的情况
//注意 经过上一次加工这里已经没有电话号码重复的了
for (Text value : values) {
//调换 KV 位置,反向写出
context.write(value,key);
}
}
}
Combiner
这个可以有可以没有,目的是减轻reduce工作量
(1)Combiner是MR程序中Mapper和Reducer之外的一种组件。
(2)Combiner组件的父类就是Reducer。
(3)Combiner和Reducer的区别在于运行的位置 Combiner是在每一个MapTask所在的节点运行;
(4)Combiner的意义就是对每一个MapTask的输出进行局部汇总,以减小网络传输量。 (5)Combiner能够应用的前提是不能影响最终的业务逻辑,而且,Combiner的输出kv 应该跟Reducer的输入kv类型要对应起来。
Mapper 3 5 7 ->(3+5+7)/3=5 2 6 ->(2+6)/2=4 Reducer (3+5+7+2+6)/5=23/5 不等于 (5+4)/2=9/2

让数据在map就变成这样
自定义一个 Combiner 继承 Reducer,重写 Reduce 方法
public class WordCountCombiner extends Reducer {
private IntWritable outV = new IntWritable();
@Override
protected void reduce(Text key, Iterable values, Context context) throws IOException, InterruptedException {
int sum = 0; for (IntWritable value : values) { sum += value.get(); } outV.set(sum); context.write(key,outV); } }
(b)在 Job 驱动类中设置: job.setCombinerClass(WordCountCombiner.class);
因为该文件代码和想实现的功能与reduce一模一样 所以想使用这个组件可以直接这样使用
// 指定需要使用 Combiner,以及用哪个类作为 Combiner 的逻辑 job.setCombinerClass(WordCountReducer.class);
学习时间:9:32 到11:35 12:15到15:23
浙公网安备 33010602011771号