1 排序概述
- 排序是MapReduce框架中最重要的操作之一.
- MapTask和ReduceTask均会对数据按照key进行排序。该操作属于Hadoop的默认行为。任何应用程序中的数据均会被排序,而不管逻辑上是否需要。
- 默认排序是按照字典顺序排序,且实现该排序的方法是快速排序。
- 对于MapTask,它会将处理的结果暂时放到环形缓冲区中,当环形缓冲区使用率达到一定阈值后,再对缓冲区中的数据进行一次快速排序,并将这些有序数据溢写到磁盘上,而当数据处理完毕后,它会对磁盘上所有文件进行归并排序。
- 对于ReduceTask,它从每个MapTask上远程拷贝相应的数据文件,如果文件大小超过一定阈值,则溢写磁盘上,否则存储在内存中。如果磁盘上文件数目达到一定阈值,则进行一次归并排序以生成一个更大文件;如果内存中文件大小或者数目超过一定阈值,则进行一次合并后将数据溢写到磁盘上。当所有数据拷贝完毕后,ReduceTask统一对内存和磁盘上的所有数据进行一次归并排序。
2 排序分类
(1)部分排序
MapReduce根据输入记录的键对数据集排序。保证输出的每个文件内部有序。
(2)全排序
最终输出结果只有一个文件,且文件内部有序。实现方式是只设置一个ReduceTask。但该方法在处理大型文件时效率极低,因为一台机器处理所有文件,完全丧失了MapReduce所提供的并行架构。
(3)辅助排序:(GroupingComparator分组)
在Reduce端对key进行分组。应用于:在接收的key为bean对象时,想让一个或几个字段相同(全部字段比较不相同)的key进入到同一个reduce方法时,可以采用分组排序。
(4)二次排序
在自定义排序过程中,如果compareTo中的判断条件为两个即为二次排序。
3 部分排序
在https://blog.youkuaiyun.com/andyonlines/article/details/104531313中的分区案例就是部分排序,它是每个分区内部有序.分区排序的实现自定义的数据类要实现WritableComparable接口,并在compareTo函数中定义自己的排序规则.
4 全排序
在https://blog.youkuaiyun.com/andyonlines/article/details/104531313中的分区案例的driver类中去掉下面两行:
job.setPartitionerClass(MyPartitioner.class);
job.setNumReduceTasks(5);
使程序只生产一个分区和一个文件文件.
5 GroupingComparator分组(辅助排序)
对Reduce阶段的数据根据某一个或几个字段进行分组。
分组排序步骤:
(1)自定义类继承WritableComparator
(2)重写compare()方法
(3)创建一个构造将比较对象的类传给父类
5.1 GroupingComparator分组 的实例
1.需求
| 订单id | 商品id | 成交金额 |
|---|---|---|
| 0000001 | Pdt_01 | 222.8 |
| Pdt_02 | 33.8 | |
| 0000002 | Pdt_03 | 522.8 |
| Pdt_04 | 122.4 | |
| Pdt_05 | 722.4 | |
| 0000003 | Pdt_06 | 232.8 |
| Pdt_02 | 33.8 |
需要求出每一个订单中最贵的商品.
2.需求分析
(1)利用“订单id和成交金额”作为key,可以将Map阶段读取到的所有订单数据按照id升序排序,如果id相同再按照金额降序排序,发送到Reduce。
(2)在Reduce端利用groupingComparator将订单id相同的kv聚合成组,然后取第一个即是该订单中最贵商品,
- 代码
GroupBean 类
package andy.group;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.WritableComparable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class GroupBean implements WritableComparable<GroupBean> {
private int group_id;
private String pdt_id;
private double price;
public GroupBean() {
super();
}
public GroupBean(int group_id, String pdt_id, double price) {
set(group_id, pdt_id,price);
}
@Override
public int compareTo(GroupBean o) {
int result = 0;
if (this.group_id > o.getGroup_id()){
result = -1;
}else if(this.group_id < o.getGroup_id()){
result = 1;
}else{
result = this.price > o.getGroup_id() ? -1 : 1;
}
return result;
}
@Override
public void write(DataOutput out) throws IOException {
out.writeInt(group_id);
out.writeUTF(pdt_id);
out.writeDouble(price);
}
@Override
public void readFields(DataInput in) throws IOException {
this.group_id = in.readInt();
this.pdt_id = in.readUTF();
this.price = in.readDouble();
}
public int getGroup_id() {
return group_id;
}
public void setGroup_id(int group_id) {
this.group_id = group_id;
}
public String getPdt_id() {
return pdt_id;
}
public void setPdt_id(String pdt_id) {
this.pdt_id = pdt_id;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public void set(int group_id, String pdt_id, double price){
this.group_id = group_id;
this.pdt_id = pdt_id;
this.price = price;
}
@Override
public String toString() {
return group_id + "\t" + pdt_id + "\t"+ price + "\t";
}
}
GpMapper 类
package andy.group;
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.io.IOException;
public class GpMapper extends Mapper<LongWritable, Text,GroupBean, NullWritable> {
GroupBean k = new GroupBean();
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
String [] line = value.toString().split("\t");
//System.out.println(line[0] + "--" + line[2]);
k.set(Integer.parseInt(line[0]),line[1],Double.parseDouble(line[2]));
//k.set(1,line[1],0.0);
context.write(k,NullWritable.get());
}
}
GpGoup 类,作为分组器
package andy.group;
import org.apache.hadoop.io.WritableComparable;
import org.apache.hadoop.io.WritableComparator;
public class GpGoup extends WritableComparator {
public GpGoup() {
super(GroupBean.class,true);
}
@Override
public int compare(WritableComparable a, WritableComparable b) {
GroupBean g1 = (GroupBean) a;
GroupBean g2 = (GroupBean) b;
int result = 0;
if (g1.getGroup_id() > g2.getGroup_id()) {
result = -1;
} else if (g1.getGroup_id() < g2.getGroup_id()) {
result = 1;
}
return result;
}
}
GpReducer 类
package andy.group;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class GpReducer extends Reducer<GroupBean, NullWritable,GroupBean, NullWritable> {
@Override
protected void reduce(GroupBean key, Iterable<NullWritable> values, Context context) throws IOException, InterruptedException {
context.write(key,NullWritable.get());
}
}
GropDriver 类
package andy.group;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.NullWritable;
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 GropDriver {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
if (args == null || args.length < 2)
args = new String[]{"E:\\temp\\input\\group", "E:\\temp\\output"};
Path path = new Path(args[1]);
Configuration conf = new Configuration();
FileSystem fs = path.getFileSystem(conf);
Job job = Job.getInstance(conf);
try {
if (fs.exists(path))
fs.delete(path, true);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} finally {
fs.close();
}
job.setJarByClass(GropDriver.class);
job.setMapperClass(GpMapper.class);
job.setReducerClass(GpReducer.class);
job.setMapOutputKeyClass(GroupBean.class);
job.setMapOutputValueClass(NullWritable.class);
job.setOutputKeyClass(GroupBean.class);
job.setOutputValueClass(NullWritable.class);
//设置分组器
job.setGroupingComparatorClass(GpGoup.class);
FileInputFormat.setInputPaths(job, args[0]);
FileOutputFormat.setOutputPath(job, path);
boolean b = job.waitForCompletion(true);
System.exit(b ? 0 : 1);
}
}
6 Combiner合并
(1)Combiner是MR程序中Mapper和Reducer之外的一种组件。
(2)Combiner组件的父类就是Reducer。
(3)Combiner和Reducer的区别在于运行的位置
Combiner是在每一个MapTask所在的节点运行;
Reducer是接收全局所有Mapper的输出结果;
(4)Combiner的意义就是对每一个MapTask的输出进行局部汇总,以减小网络传输量。
(5)Combiner能够应用的前提是不能影响最终的业务逻辑,而且,Combiner的输出kv应该跟Reducer的输入kv类型要对应起来。
(6)自定义Combiner实现步骤
(a)自定义一个Combiner继承Reducer,重写Reduce方法
public class WordcountCombiner extends Reducer<Text, IntWritable, Text,IntWritable>{
@Override
protected void reduce(Text key, Iterable<IntWritable> values,Context context) throws IOException, InterruptedException {
// 1 汇总操作
int count = 0;
for(IntWritable v :values){
count += v.get();
}
// 2 写出
context.write(key, new IntWritable(count));
}
}
(b)在Job驱动类中设置:
job.setCombinerClass(WordcountCombiner.class);
博客详细介绍了MapReduce的排序机制,包括部分排序、全排序、辅助排序(GroupingComparator)以及Combiner合并。文章强调了排序的重要性,MapTask和ReduceTask如何对数据进行排序,以及如何通过自定义GroupingComparator进行分组。此外,还阐述了Combiner的作用,即进行局部汇总以减少网络传输量。
1811

被折叠的 条评论
为什么被折叠?



