订单数据如下(订单id, 商品id, 成交金额 ):
Order_0000001,Pdt_01,222.8
Order_0000001,Pdt_05,25.8
Order_0000002,Pdt_05,325.8
Order_0000002,Pdt_03,522.8
Order_0000002,Pdt_04,122.4
Order_0000003,Pdt_01,222.8
Order_0000003,Pdt_01,322.8
需求:
找出订单号相同的最大成交金额的订单信息,比如订单号Order_0000001的最大交易额为222.8
思路1:
在map中将订单号作为key,其他数据封装bean 输出,在reduce中对bean中的成交金额进行排序,筛选出最大的成交金额。
思路2:
我们可以再map中将订单id跟订单金额封装成一个bean作为key,null作为value输出,输入到reduce之前,这里用前面介绍的,让bean实现WritableComparable然后重写compareTo(用来排序)方法,使得不同订单号的订单先按照订单号由小到大排序,相同订单号的订单按照成交额倒排序。但是存在一个问题,此时由于bean作为key那么不一定能保证传到同一个reduce中,都不在一个reduce中了那肯定也不能去比较订单号相同的记录了,这个时候就需要自己指定自定义的Partitioner(用来分区)了来使得相同订单id的订单记录传到同一个reduce中。最后由于key不同那么传到reduce中的时候,即使是相同订单id的bean也只会一次一次传到reduce,那么还怎么取比较呢?此时就要使用自定义GroupingComparator(用来分组)了,指定只要是订单id相同的bean都看为一组,拿Order_0000001的三条记录来说(三个bean),由于会被看成一组,那么传到reduce方法中的时候,key只传第一个bean,由于我们前面相同订单id的bean是按照成交额由高到低排的,那么这个时候传进来的key就肯定是成交额最大的bean。
考虑效率方面的问题,我们实现第二种思路。
程序实现
自定义Bean
public class OrderGroupingComparator extends WritableComparator{
//传入作为key的bean的class类型,以及指定需要让框架做反射获取实例对象
protected OrderGroupingComparator(){
//不能丢,注册某个bean 要不要实例化 因为拿来的时候是序列化的结果
//实例化后才能去比
super(InfoBean.class,true);
}
//相同的订单id的bean看做一组(value值作为迭代器中数据),传到reduce中的时候取第一个bean
@Override
public int compare(WritableComparable a, WritableComparable b) {
InfoBean bean1 = (InfoBean)a;
InfoBean bean2 = (InfoBean)b;
return bean1.getOrderId().compareTo(bean2.getOrderId());
}
}
自定义Partitioner
public class OrderPartitioner extends Partitioner<InfoBean, NullWritable>{
//分区方法,返回值表示分区号,与reducetask对应
@Override
public int getPartition(InfoBean key, NullWritable value, int numReduceTasks) {
//使相同id的订单bean交给同一个reducetask
//这里参照HashPartitioner来写
return (key.getOrderId().hashCode()& Integer.MAX_VALUE) % numReduceTasks;
}
}
自定义GroupingComparator
public class OrderGroupingComparator extends WritableComparator{
//传入作为key的bean的class类型,以及指定需要让框架做反射获取实例对象
protected OrderGroupingComparator(){
//不能丢,注册某个bean 要不要实例化 因为拿来的时候是序列化的结果
//实例化后才能去比
super(InfoBean.class,true);
}
//相同的订单id的bean看做一组(value值作为迭代器中数据),传到reduce中的时候取第一个bean
@Override
public int compare(WritableComparable a, WritableComparable b) {
InfoBean bean1 = (InfoBean)a;
InfoBean bean2 = (InfoBean)b;
return bean1.getOrderId().compareTo(bean2.getOrderId());
}
}
主程序
public class MaxPaymentWithOrder {
static class MaxPaymentWithOrderMapper extends Mapper<LongWritable, Text, InfoBean, NullWritable>{
InfoBean bean = new InfoBean();
//读取一行数据,封装成bean作为key写出
//传入格式Order_0000001,Pdt_01,222.8
@Override
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
String line = value.toString();
String[] infos = line.split(",");
bean.setInfoBean(new Text(infos[0]), new DoubleWritable(Double.parseDouble(infos[2])));
context.write(bean, NullWritable.get());
}
}
static class MaxPaymentWithOrderReducer extends Reducer<InfoBean, NullWritable, InfoBean, NullWritable>{
//将相同id的订单看成一组传进来(使用自定义GroupingComparator实现)
//而key只会取第一个
@Override
protected void reduce(InfoBean key, Iterable<NullWritable> value,Context context)
throws IOException, InterruptedException {
context.write(key, NullWritable.get());
}
}
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = Job.getInstance(conf);
//jar包位置
job.setJarByClass(MaxPaymentWithOrder.class);
job.setMapperClass(MaxPaymentWithOrderMapper.class);
job.setReducerClass(MaxPaymentWithOrderReducer.class);
job.setMapOutputKeyClass(InfoBean.class);
job.setMapOutputValueClass(NullWritable.class);
//设置最终输出类型
job.setOutputKeyClass(InfoBean.class);
job.setOutputValueClass(NullWritable.class);
//设置reducetask数量
job.setNumReduceTasks(2);
//指定Partitioner
job.setPartitionerClass(OrderPartitioner.class);
//指定GroupingComparator
job.setGroupingComparatorClass(OrderGroupingComparator.class);
FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
boolean ex = job.waitForCompletion(true);
System.exit(ex?0:1);
}
}
原文:https://blog.youkuaiyun.com/qq_37334135/article/details/78255411