|NO.Z.00038|——————————|BigDataEnd|——|Hadoop&MapReduce.V11|——|Hadoop.v11|Shutffle机制详解之全排序|分区排序|
一、[Shutffle机制详解之全排序|分区排序]:MapReduce中的排序
### --- 排序是MapReduce框架中最重要的操作之一。
~~~ MapTask和ReduceTask均会对数据按照key进行排序。该操作属于Hadoop的默认行为。
~~~ 任何应用程序中的数据均会被排序,而不管逻辑.上是否需要。
~~~ 默认排序是按照字典顺序排序,且实现该排序的方法是快速排序。
### --- MapTask
~~~ 它会将处理的结果暂时放到环形缓冲区中,当环形缓冲区使用率达到一定阈值后,
~~~ 再对缓冲区中的数据进行一次快速排序,并将这些有序数据溢写到磁盘上,溢写完毕后,
~~~ 它会对磁盘上所有文件进行归并排序。
### --- ReduceTask
~~~ 当所有数据拷贝完毕后,ReduceTask统-对内存和磁盘上的所有数据进行一次归并排序。
二、常用的排序方式
### --- 部分排序.
~~~ MapReduce根据输入记录的键对数据集排序。保证输出的每个文件内部有序。
### --- 全排序
~~~ 最终输出结果只有一个文件,且文件内部有序。实现方式是只设置- -个ReduceTask。
~~~ 但该方法在处理大型文件时效率极低,因为- -台机器处理所有文件,
~~~ 完全丧失了MapReduce所提供的并行架构。
### --- 辅助排序: ( GroupingComparator分组)
~~~ 在Reduce端对key进行分组。应用于:在接收的key为bean对象时,
~~~ 想让一个或几个字段相同(全部字段比较不相同)的key进入到同一个reduce方法时,
~~~ 可以采用分组排序。
### --- 二次排序.
~~~ 在自定义排序过程中,如果compareTo中的判断条件为两个即为二次排序。
一、WritableComparable
### --- WritablComparable
~~~ Bean对象如果作为Map输出的key时,
~~~ 需要实现WritableComparable接口并重写compareTo方法指定排序规则
二、全排序
### --- 全排序
~~~ 基于统计的播放时长案例的输出结果对总时长进行排序
~~~ 实现全局排序只能设置一个ReduceTask!!
~~~ 播放时长案例输出结果
00fdaf3 33180 33420 00fdaf3 66600
00wersa4 30689 35191 00wersa4 65880
0a0fe2 43085 44254 0a0fe2 87339
0ad0s7 31702 29183 0ad0s7 60885
0sfs01 31883 29101 0sfs01 60984
a00df6s 33239 36882 a00df6s 70121
adfd00fd5 30727 31491 adfd00fd5 62218
### --- 需求分析
~~~ 如何设计map()方法输出的key,value
~~~ MR框架中shuffle阶段的排序是默认行为,不管你是否需要都会进行排序。
~~~ key:把所有字段封装成为一个bean对象,并且指定bean对象作为key输出,
~~~ 如果作为key输出,需要实现排序接口,指定自己的排序规则;
三、具体步骤
### --- Mapper
~~~ 读取结果文件,按照制表符进行切分
~~~ 解析出相应字段封装为SpeakBean
~~~ SpeakBean实现WritableComparable接口重写compareTo方法
~~~ map()方法输出kv;key-->SpeakBean,value-->NullWritable.get()
### --- Reducer
~~~ 循环遍历输出
四、代码实现:创建一个sort项目
### --- Mapper代码
package com.yanqi.mr.sort;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.awt.image.BandCombineOp;
import java.io.IOException;
public class SortMapper extends Mapper<LongWritable, Text, SpeakBean, NullWritable> {
final SpeakBean bean = new SpeakBean();
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
//1 读取一行文本,转为字符串,切分
final String[] fields = value.toString().split("\t");
//2 解析出各个字段封装成SpeakBean对象
bean.setDeviceId(fields[0]);
bean.setSelfDrutation(Long.parseLong(fields[1]));
bean.setThirdPartDuration(Long.parseLong(fields[2]));
bean.setSumDuration(Long.parseLong(fields[4]));
//3 SpeakBean作为key输出
context.write(bean, NullWritable.get());
}
}
### --- Reducer
package com.yanqi.mr.sort;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class SortReducer extends Reducer<SpeakBean, NullWritable, SpeakBean, NullWritable> {
//reduce方法的调用是相同key的value组成一个集合调用一次
/*
java中如何判断两个对象是否相等?
根据equals方法,比较还是地址值
*/
@Override
protected void reduce(SpeakBean key, Iterable<NullWritable> values, Context context) throws IOException, InterruptedException {
//讨论按照总流量排序这件事情,还需要在reduce端处理吗?因为之前已经利用mr的shuffle对数据进行了排序
//为了避免前面compareTo方法导致总流量相等被当成对象相等,而合并了key,所以遍历values获取每个key(bean对象)
for (NullWritable value : values) { //遍历value同时,key也会随着遍历。
context.write(key, value);
}
}
}
### --- Bean对象实现WritableComparable接口
package com.yanqi.mr.sort;
import org.apache.hadoop.io.WritableComparable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Objects;
//因为这个类的实例对象要作为map输出的key,所以要实现writablecomparalbe接口
public class SpeakBean implements WritableComparable<SpeakBean> {
//定义属性
private Long selfDrutation;//自有内容播放时长
private Long thirdPartDuration;//第三方内容播放时长
private String deviceId;//设备id
private Long sumDuration;//总时长
//准备构造方法
public SpeakBean() {
}
public SpeakBean(Long selfDrutation, Long thirdPartDuration, String deviceId, Long sumDuration) {
this.selfDrutation = selfDrutation;
this.thirdPartDuration = thirdPartDuration;
this.deviceId = deviceId;
this.sumDuration = sumDuration;
}
public Long getSelfDrutation() {
return selfDrutation;
}
public void setSelfDrutation(Long selfDrutation) {
this.selfDrutation = selfDrutation;
}
public Long getThirdPartDuration() {
return thirdPartDuration;
}
public void setThirdPartDuration(Long thirdPartDuration) {
this.thirdPartDuration = thirdPartDuration;
}
public String getDeviceId() {
return deviceId;
}
public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
public Long getSumDuration() {
return sumDuration;
}
public void setSumDuration(Long sumDuration) {
this.sumDuration = sumDuration;
}
//序列化方法
@Override
public void write(DataOutput out) throws IOException {
out.writeLong(selfDrutation);
out.writeLong(thirdPartDuration);
out.writeUTF(deviceId);
out.writeLong(sumDuration);
}
//反序列化方法
@Override
public void readFields(DataInput in) throws IOException {
this.selfDrutation = in.readLong();
this.thirdPartDuration = in.readLong();
this.deviceId = in.readUTF();
this.sumDuration = in.readLong();
}
//指定排序规则,我们希望按照总时长进行排序
@Override
public int compareTo(SpeakBean o) { //返回值三种:0:相等 1:小于 -1:大于
System.out.println("compareTo 方法执行了。。。");
//指定按照bean对象的总时长字段的值进行比较
if (this.sumDuration > o.sumDuration) {
return -1;
} else if (this.sumDuration < o.sumDuration) {
return 1;
} else {
return 0; //加入第二个判断条件,二次排序
}
}
@Override
public boolean equals(Object o) {
System.out.println("equals方法执行了。。。");
return super.equals(o);
}
@Override
public int hashCode() {
return Objects.hash(getSelfDrutation(), getThirdPartDuration(), getDeviceId(), getSumDuration());
}
@Override
public String toString() {
return selfDrutation +
"\t" + thirdPartDuration +
"\t" + deviceId + '\t' +
sumDuration
;
}
}
### --- Driver
package com.yanqi.mr.sort;
import com.yanqi.mr.wc.WordCountDriver;
import com.yanqi.mr.wc.WordCountMapper;
import com.yanqi.mr.wc.WordCountReducer;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.NullWritable;
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 SortDriver {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
/*
1. 获取配置文件对象,获取job对象实例
2. 指定程序jar的本地路径
3. 指定Mapper/Reducer类
4. 指定Mapper输出的kv数据类型
5. 指定最终输出的kv数据类型
6. 指定job处理的原始数据路径
7. 指定job输出结果路径
8. 提交作业
*/
// 1. 获取配置文件对象,获取job对象实例
final Configuration conf = new Configuration();
final Job job = Job.getInstance(conf, "SortDriver");
// 2. 指定程序jar的本地路径
job.setJarByClass(SortDriver.class);
// 3. 指定Mapper/Reducer类
job.setMapperClass(SortMapper.class);
job.setReducerClass(SortReducer.class);
// 4. 指定Mapper输出的kv数据类型
job.setMapOutputKeyClass(SpeakBean.class);
job.setMapOutputValueClass(NullWritable.class);
// 5. 指定最终输出的kv数据类型
job.setOutputKeyClass(SpeakBean.class);
job.setOutputValueClass(NullWritable.class);
//指定reduceTask的数量,默认是1个
job.setNumReduceTasks(1);
// 6. 指定job处理的原始数据路径
//import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
//import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
FileInputFormat.setInputPaths(job, new Path("E:\\speak\\out")); //指定读取数据的原始路径
// 7. 指定job输出结果路径
FileOutputFormat.setOutputPath(job, new Path("e:\\speak\\sortout")); //指定结果数据输出路径
// 8. 提交作业
final boolean flag = job.waitForCompletion(true);
//jvm退出:正常退出0,非0值则是错误退出
System.exit(flag ? 0 : 1);
}
}
四、编译打印

### --- 总结
~~~ 自定义对象作为Map的key输出时,需要实现WritableComparable接口,
~~~ # 排序:重写compareTo()方法,序列以及反序列化方法再次理解reduce()方法的参数;
~~~ reduce()方法是map输出的kv中key相同的kv中的v组成一个集合调用一次reduce()方法,
~~~ 选择遍历values得到所有的key.
~~~ 默认reduceTask数量是1个;
~~~ 对于全局排序需要保证只有一个reduceTask!!
Walter Savage Landor:strove with none,for none was worth my strife.Nature I loved and, next to Nature, Art:I warm'd both hands before the fire of life.It sinks, and I am ready to depart
——W.S.Landor
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通