指对Reduce阶段的数据根据某一个或几个字段进行分组。

案例

需求

有如下订单数据



现在需要找出每一个订单中最贵的商品,如图

需求分析

  • 利用“订单id和成交金额”作为key,可以将Map阶段读取到的所有订单数据先按照订单id(升降序都可以),再按照acount(降序)排序,发送到Reduce。

  • 在Reduce端利用groupingComparator将订单id相同的kv聚合成组,然后取第一个成交金额即是最大值(若有多个成交金额并排第一,则都输出)。

  • Mapper阶段主要做三件事:

    keyin-valuein

    map()

    keyout-valueout

  • 期待shuffle之后的数据:

    10000001 Pdt_02 222.8

    10000001 Pdt_01 222.8

    10000001 Pdt_05 25.8

    10000002 Pdt_06 722.4

    10000002 Pdt_03 522.8

    10000002 Pdt_04 122.4

    10000003 Pdt_01 232.8

    10000003 Pdt_01 33.8

  • Reducer阶段主要做三件事:

    keyin-valuein

    reduce()

    keyout-valueout

  • 进入Reduce需要考虑的事

  1. 获取分组比较器,如果没设置默认使用MapTask排序时key的比较器
  2. 默认的比较器比较策略不符合要求,它会将orderId一样且acount一样的记录才认为是一组的
  3. 自定义分组比较器,只按照orderId进行对比,只要OrderId一样,认为key相等,这样可以将orderId相同的分到一个组!

    在组内去第一个最大的即可

编写程序

利用“订单id和成交金额”作为key,所以把每一行记录封装为bean。由于需要比较ID,所以实现了WritableComparable接口

OrderBean.java

public class OrderBean implements WritableComparable<OrderBean>{

	private String orderId;
private String pId;
private Double acount; public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public String getpId() {
return pId;
}
public void setpId(String pId) {
this.pId = pId;
}
public Double getAcount() {
return acount;
}
public void setAcount(Double acount) {
this.acount = acount;
}
public OrderBean() { } @Override
public String toString() {
return orderId + "\t" + pId + "\t" + acount ;
} @Override
public void write(DataOutput out) throws IOException {
out.writeUTF(orderId);
out.writeUTF(pId);
out.writeDouble(acount);
} @Override
public void readFields(DataInput in) throws IOException {
orderId=in.readUTF();
pId=in.readUTF();
acount=in.readDouble();
} // 二次排序,先按照orderid排序(升降序都可以),再按照acount(降序)排序
@Override
public int compareTo(OrderBean o) { //先按照orderid排序升序排序
int result=this.orderId.compareTo(o.getOrderId()); if (result==0) {//订单ID相同,就比较成交金额的大小
//再按照acount(降序)排序
result=-this.acount.compareTo(o.getAcount()); } return result;
}
}

自定义比较器,可以通过两种方法:

  • 继承WritableCompartor
  • 实现RawComparator

MyGroupingComparator.java

//实现RawComparator
public class MyGroupingComparator implements RawComparator<OrderBean>{ private OrderBean key1=new OrderBean();
private OrderBean key2=new OrderBean();
private DataInputBuffer buffer=new DataInputBuffer(); @Override
public int compare(OrderBean o1, OrderBean o2) {
return o1.getOrderId().compareTo(o2.getOrderId());
} @Override
public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) { try {
buffer.reset(b1, s1, l1); // parse key1
key1.readFields(buffer); buffer.reset(b2, s2, l2); // parse key2
key2.readFields(buffer); buffer.reset(null, 0, 0); // clean up reference
} catch (IOException e) {
throw new RuntimeException(e);
} return compare(key1, key2);
} }

MyGroupingComparator2.java

 //继承WritableCompartor
public class MyGroupingComparator2 extends WritableComparator{ public MyGroupingComparator2() {
super(OrderBean.class,null,true);
} public int compare(WritableComparable a, WritableComparable b) {
OrderBean o1=(OrderBean) a;
OrderBean o2=(OrderBean) b;
return o1.getOrderId().compareTo(o2.getOrderId());
}
}

OrderMapper.java

public class OrderMapper extends Mapper<LongWritable, Text, OrderBean, NullWritable>{

	private OrderBean out_key=new OrderBean();
private NullWritable out_value=NullWritable.get(); @Override
protected void map(LongWritable key, Text value,
Mapper<LongWritable, Text, OrderBean, NullWritable>.Context context)
throws IOException, InterruptedException { String[] words = value.toString().split("\t"); out_key.setOrderId(words[0]);
out_key.setpId(words[1]);
out_key.setAcount(Double.parseDouble(words[2])); context.write(out_key, out_value); }
}

OrderReducer.java

public class OrderReducer extends Reducer<OrderBean, NullWritable, OrderBean, NullWritable>{

	/*
* OrderBean key-NullWritable nullWritable在reducer工作期间,
* 只会实例化一个key-value的对象!
* 每次调用迭代器迭代下个记录时,使用反序列化器从文件中或内存中读取下一个key-value数据的值,
* 封装到之前OrderBean key-NullWritable nullWritable在reducer的属性中
*/
@Override
protected void reduce(OrderBean key, Iterable<NullWritable> values,
Reducer<OrderBean, NullWritable, OrderBean, NullWritable>.Context context)
throws IOException, InterruptedException { Double maxAcount = key.getAcount(); for (NullWritable nullWritable : values) { if (!key.getAcount().equals(maxAcount)) {
break;
}
//复合条件的记录
context.write(key, nullWritable); } }
}

OrderBeanDriver.java

public class OrderBeanDriver {

	public static void main(String[] args) throws Exception {

		Path inputPath=new Path("E:\\mrinput\\groupcomparator");
Path outputPath=new Path("e:/mroutput/groupcomparator"); //作为整个Job的配置
Configuration conf = new Configuration(); //保证输出目录不存在
FileSystem fs=FileSystem.get(conf); if (fs.exists(outputPath)) { fs.delete(outputPath, true); } // ①创建Job
Job job = Job.getInstance(conf); // ②设置Job
// 设置Job运行的Mapper,Reducer类型,Mapper,Reducer输出的key-value类型
job.setMapperClass(OrderMapper.class);
job.setReducerClass(OrderReducer.class); // Job需要根据Mapper和Reducer输出的Key-value类型准备序列化器,通过序列化器对输出的key-value进行序列化和反序列化
// 如果Mapper和Reducer输出的Key-value类型一致,直接设置Job最终的输出类型 job.setOutputKeyClass(OrderBean.class);
job.setOutputValueClass(NullWritable.class); // 设置输入目录和输出目录
FileInputFormat.setInputPaths(job, inputPath);
FileOutputFormat.setOutputPath(job, outputPath); // 设置自定义的分组比较器
job.setGroupingComparatorClass(MyGroupingComparator2.class); // ③运行Job
job.waitForCompletion(true); }
}

输出结果

MapReduce之GroupingComparator分组(辅助排序、二次排序)的更多相关文章

  1. mapreduce编程(一)-二次排序

    转自:http://blog.csdn.net/heyutao007/article/details/5890103 mr自带的例子中的源码SecondarySort,我重新写了一下,基本没变. 这个 ...

  2. spark分组统计及二次排序案例一枚

    组织数据形式: aa 11 bb 11 cc 34 aa 22 bb 67 cc 29 aa 36 bb 33 cc 30 aa 42 bb 44 cc 49 需求: 1.对上述数据按key值进行分组 ...

  3. Spark基础排序+二次排序(java+scala)

    1.基础排序算法 sc.textFile()).reduceByKey(_+_,).map(pair=>(pair._2,pair._1)).sortByKey(false).map(pair= ...

  4. Hadoop MapReduce编程 API入门系列之二次排序(十六)

    不多说,直接上代码. -- ::, INFO [org.apache.hadoop.metrics.jvm.JvmMetrics] - Initializing JVM Metrics with pr ...

  5. Hadoop MapReduce 二次排序原理及其应用

    关于二次排序主要涉及到这么几个东西: 在0.20.0 以前使用的是 setPartitionerClass setOutputkeyComparatorClass setOutputValueGrou ...

  6. 详细讲解MapReduce二次排序过程

    我在15年处理大数据的时候还都是使用MapReduce, 随着时间的推移, 计算工具的发展, 内存越来越便宜, 计算方式也有了极大的改变. 到现在再做大数据开发的好多同学都是直接使用spark, hi ...

  7. Haoop MapReduce 的Partition和reduce端的二次排序

    先贴一张原理图(摘自hadoop权威指南第三版) 实际中看了半天还是不太理解其中的Partition,和reduce端的二次排序,最终根据实验来结果来验证自己的理解 1eg 数据如下 20140101 ...

  8. Hadoop案例(八)辅助排序和二次排序案例(GroupingComparator)

    辅助排序和二次排序案例(GroupingComparator) 1.需求 有如下订单数据 订单id 商品id 成交金额 0000001 Pdt_01 222.8 0000001 Pdt_05 25.8 ...

  9. Hadoop Mapreduce分区、分组、二次排序过程详解[转]

    原文地址:Hadoop Mapreduce分区.分组.二次排序过程详解[转]作者: 徐海蛟 教学用途 1.MapReduce中数据流动   (1)最简单的过程:  map - reduce   (2) ...

随机推荐

  1. 【区间DP】低价回文

    [区间DP]低价回文 标签(空格分隔): 区间DP 回文词 [题目描述] 追踪每头奶牛的去向是一件棘手的任务,为此农夫约翰安装了一套自动系统.他在每头牛身上安装了一个电子身份标签,当奶牛通过扫描器的时 ...

  2. Codeforces 1215D Ticket Game 题解

    Codeforces 1215D Ticket Game 原题 题目 Monocarp and Bicarp live in Berland, where every bus ticket consi ...

  3. 洛谷 P3574 [POI2014]FAR-FarmCraft

    题目传送门 题目描述 输入输出格式 输入格式: 输出格式: 一行,包含一个整数,代表题目中所说的最小时间. 输入输出样例 样例输入 样例输出 提示 分析 我们设f[x]为遍历完以x为根的子树且将这棵子 ...

  4. css3 文本行的斑马线

    背景知识 CSS 渐变, background-size ,“条纹背景”,“灵活的背景定位 难题 几年前,在刚刚获得 :nth-child() / :nth-of-type() 伪类之后,我们最常用其 ...

  5. if-else和三目运算符 ? : 的对比

    今天的地铁思考让我想起一个之前学C语言的时候一直没有验证的事情:既生瑜何生亮? 平时写代码的时候,似乎并没有太多的关注我应该选用什么条件判断语句,感觉判断条件或者两条支路语句复杂就本能地if-else ...

  6. redis(二十一):Redis 架构模式实现(哨兵)

    先了解一下哨兵都 做了什么工作:Redis 的 Sentinel 系统用于管理多个 Redis 服务器(instance), 该系统执行以下三个任务: 监控(Monitoring): Sentinel ...

  7. 深度理解SpringIOC,面试你根本不需要慌!

    文章已托管到GitHub,大家可以去GitHub查看阅读,欢迎老板们前来Star! 搜索关注微信公众号 码出Offer 领取各种学习资料! 深度理解Spring IOC(控制反转) 一.IOC概述 I ...

  8. vue + echart 实现中国地图 和 省市地图(可切换省份)

    一.中国地图 1.先导入echarts,然后再main.js里引入echarts // 引入echartsimport echarts from 'echarts'Vue.prototype.$ech ...

  9. 占个坑 未来学qt的时候专用

    今天看了一个大佬发了一个上位机图片便向大佬问道 ”上位机是用什么软件做的“大佬抛下一句qt ,在业界内很通用,windows和linux通吃,便让我萌生了一个想法,去学qt.虽说上位机时常听到,但是自 ...

  10. ‘100%’wuxiao

    有时候设置控件的width:’100%‘无效,  群友解释, 因为父控件的大小要靠自空间撑起来,确定    ??????