|NO.Z.00039|——————————|BigDataEnd|——|Hadoop&MapReduce.V12|——|Hadoop.v12|shuffle机制详解之GroupingComparator分组|
一、分区排序(默认的分区规则,区内有序)
~~~ 分区排序(默认的分区规则,区内有序)
~~~ [shuffle机制详解之GroupingComparator分组]
### --- GroupingComparator
~~~ GroupingComparator是mapreduce当中reduce端的一个功能组件,
~~~ 主要的作用是决定哪些数据作为一组,调用一次reduce的逻辑,默认是每个不同的key,
~~~ 作为多个不同的组,每个组调用一次reduce逻辑,
~~~ 我们可以自定义GroupingComparator实现不同的key作为同一个组,调用一次reduce逻辑。
二、需求:原始数据:需要求出每一个订单中成交金额最大的一笔交易。
订单id | 商品id | 成交金额 |
Order_0000001 | Pdt_01 | 222.8 |
Order_0000001 | Pdt_05 | 25.8 |
Order_0000002 | Pdt_03 | 522.8 |
Order_0000002 | Pdt_04 | 122.4 |
Order_0000002 | Pdt_05 | 722.4 |
Order_0000003 | Pdt_01 | 232.8 |
### --- 实现思路
~~~ # Mapper
~~~ 读取一行文本数据,切分出每个字段;
~~~ 订单id和金额封装为一个Bean对象,Bean对象的排序规则指定为先按照订单Id排序,
~~~ 订单Id相等再按照金额降序排;
~~~ map()方法输出kv;key-->bean对象,value-->NullWritable.get();Shuffle
~~~ 指定分区器,保证相同订单id的数据去往同个分区(自定义分区器)
~~~ 指定GroupingComparator,分组规则指定只要订单Id相等则认为属于同一组;
~~~ # Reduce
~~~ 每个reduce()方法写出一组key的第一个
三、编程代码
### --- 编程代码
~~~ # OrderBean
~~~ OrderBean定义两个字段,一个字段是orderId,第二个字段是金额(注意金额一定要使用
~~~ Double或者DoubleWritable类型,否则没法按照金额顺序排序)
~~~ 排序规则指定为先按照订单Id排序,订单Id相等再按照金额降序排!!
### --- 创建项目:group
### --- WritableComparable
package com.yanqi.mr.group;
import org.apache.hadoop.io.WritableComparable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class OrderBean implements WritableComparable<OrderBean> {
private String orderId;//订单id
private Double price;//金额
public OrderBean(String orderId, Double price) {
this.orderId = orderId;
this.price = price;
}
public OrderBean() {
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
//指定排序规则,先按照订单id比较再按照金额比较,按照金额降序排
@Override
public int compareTo(OrderBean o) {
// int res = this.orderId.compareTo(o.getOrderId()); //0 1 -1
// if (res == 0) {
// //订单id相同,比较金额
// res = - this.price.compareTo(o.getPrice());
//
// }
int res = - this.price.compareTo(o.getPrice());
System.out.println(res);
return res;
}
//序列化
@Override
public void write(DataOutput out) throws IOException {
out.writeUTF(orderId);
out.writeDouble(price);
}
//反序列化
@Override
public void readFields(DataInput in) throws IOException {
this.orderId = in.readUTF();
this.price = in.readDouble();
}
//重写toString()
@Override
public String toString() {
return orderId + '\t' +
price
;
}
}
### --- 自定义分区器
### --- 保证ID相同的订单去往同个分区最终去往同一个Reduce中
package com.yanqi.mr.group;
import org.apache.avro.Schema;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class GroupReducer extends Reducer<OrderBean, NullWritable, OrderBean, NullWritable> {
//key:reduce方法的key注意是一组相同key的kv的第一个key作为传入reduce方法的key,因为我们已经指定了排序的规则
//按照金额降序排列,则第一个key就是金额最大的交易数据
//value:一组相同key的kv对中v的集合
//对于如何判断key是否相同,自定义对象是需要我们指定一个规则,这个规则通过Groupingcomaprator来指定
@Override
protected void reduce(OrderBean key, Iterable<NullWritable> values, Context context) throws IOException, InterruptedException {
//直接输出key就是金额最大的交易
context.write(key, NullWritable.get());
}
}
### --- 自定义GroupingComparator
### --- 保证id相同的订单进入一个分组中,进入分组的数据已经是按照金额降序排序。reduce()方法取出第一个即是金额最高的交易
package com.yanqi.mr.group;
import com.sun.corba.se.impl.orb.ParserTable;
import org.apache.hadoop.io.WritableComparable;
import org.apache.hadoop.io.WritableComparator;
public class CustomGroupingComparator extends WritableComparator {
public CustomGroupingComparator() {
super(OrderBean.class, true); //注册自定义的GroupingComparator接受OrderBean对象
}
//重写其中的compare方法,通过这个方法来让mr接受orderid相等则两个对象相等的规则,key相等
@Override
public int compare(WritableComparable a, WritableComparable b) { //a 和b是orderbean的对象
//比较两个对象的orderid
final OrderBean o1 = (OrderBean) a;
final OrderBean o2 = (OrderBean) b;
final int i = o1.getOrderId().compareTo(o2.getOrderId());
return i; // 0 1 -1
}
}
### --- Mapper
package com.yanqi.mr.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 GroupMapper extends Mapper<LongWritable, Text, OrderBean, NullWritable> {
OrderBean bean = new OrderBean();
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
final String[] fields = value.toString().split("\t");
//订单id与jine封装为一个orderBean
bean.setOrderId(fields[0]);
bean.setPrice(Double.parseDouble(fields[2]));
context.write(bean, NullWritable.get());
}
}
### --- Reducer
package com.yanqi.mr.group;
import org.apache.avro.Schema;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class GroupReducer extends Reducer<OrderBean, NullWritable, OrderBean, NullWritable> {
//key:reduce方法的key注意是一组相同key的kv的第一个key作为传入reduce方法的key,因为我们已经指定了排序的规则
//按照金额降序排列,则第一个key就是金额最大的交易数据
//value:一组相同key的kv对中v的集合
//对于如何判断key是否相同,自定义对象是需要我们指定一个规则,这个规则通过Groupingcomaprator来指定
@Override
protected void reduce(OrderBean key, Iterable<NullWritable> values, Context context) throws IOException, InterruptedException {
//直接输出key就是金额最大的交易
context.write(key, NullWritable.get());
}
}
### --- Driver
package com.yanqi.mr.group;
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 GroupDriver {
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, "GroupDriver");
// 2. 指定程序jar的本地路径
job.setJarByClass(GroupDriver.class);
// 3. 指定Mapper/Reducer类
job.setMapperClass(GroupMapper.class);
job.setReducerClass(GroupReducer.class);
// 4. 指定Mapper输出的kv数据类型
job.setMapOutputKeyClass(OrderBean.class);
job.setMapOutputValueClass(NullWritable.class);
// 5. 指定最终输出的kv数据类型
job.setOutputKeyClass(OrderBean.class);
job.setOutputValueClass(NullWritable.class);
//指定分区器
job.setPartitionerClass(CustomPartitioner.class);
//指定使用groupingcomparator
job.setGroupingComparatorClass(CustomGroupingComparator.class);
FileInputFormat.setInputPaths(job, new Path("E:\\teach\\hadoop框架\\资料\\data\\GroupingComparator")); //指定读取数据的原始路径
// 7. 指定job输出结果路径
FileOutputFormat.setOutputPath(job, new Path("E:\\group\\out3")); //指定结果数据输出路径
//指定reducetask的数量,不要使用默认的一个,分区效果不明显
job.setNumReduceTasks(2);
// 8. 提交作业
final boolean flag = job.waitForCompletion(true);
//jvm退出:正常退出0,非0值则是错误退出
System.exit(flag ? 0 : 1);
}
}
四、编译打印

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最大的设计失误
· 单元测试从入门到精通