Hadoop分区排序

Hadoop全排序相关

要点

2 分区排序(默认的分区规则,区内有序)

直白讲解:CustomGroupingComparator帮助我们实现Reduce分组的时候来制定我们的分组规则。然后通过Bean重写的compareTo再在组内进行排序,即实现分组排序。(注意!其实这里的分组的意义是将kv整理成Key:Value[]。即构造一个key对应的value迭代器。所以在后续的Reducer中我们直接输出Key就得到了订单中最大的金额,因为此时key为分组排序后第一个(最大金额)kv的key,而value为空)
例如:按照订单id分组,然后排序,得到每个订单中价格最高或最低的商品id或价格。
直白讲解:CustomPartitioner帮助我们实现map输出数据的分区规则。分区后,每个分区进入一个ReduceTask,每个ReduceTask输出一份文件,方便数据查询管理。(注意!这里才实现了物理上面额的分区,即根据订单id进入不同的ReduceTask)
例如:最终结果,每个订单id内容输出为一个文件,方便分类管理。

shuffle分区示意图

GroupingComparator是mapreduce当中reduce端的⼀个功能组件,主要的作⽤是决定哪些数据作为⼀组,调⽤⼀次reduce的逻辑。默认是每个不同的key,作为多个不同的组,每个组调⽤⼀次reduce逻辑,我们可以⾃定义GroupingComparator实现不同的key作为同⼀个组,调⽤⼀次reduce逻辑。

思路分析

  1. 需求
    原始数据
    在这里插入图片描述
  2. 实现思路
    Mapper
    读取⼀⾏⽂本数据,切分出每个字段;
    订单id和⾦额封装为⼀个Bean对象,Bean对象的排序规则指定为先按照订单Id排序,订单Id相等再按照⾦额降序排;
    map()⽅法输出kv;key–>bean对象,value–>NullWritable.get();
    Shuffle
    指定分区器,保证相同订单id的数据去往同个分区(⾃定义分区器)指定GroupingComparator,分组规则指定只要订单Id相等则认为属于同⼀组;
    Reduce
    每个reduce()⽅法写出⼀组key的第⼀个

代码示例

CustomGroupingComparator代码

package com.lagou.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;
        return o1.getOrderId().compareTo(o2.getOrderId()); // 0 1 -1
    }
}

CustomPartitioner代码

package com.lagou.mr.group;

import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.mapreduce.Partitioner;

public class CustomPartitioner extends Partitioner<OrderBean, NullWritable> {
    @Override
    public int getPartition(OrderBean orderBean, NullWritable nullWritable, int numPartitions) {
        //希望订单id相同的数据进入同个分区

        return (orderBean.getOrderId().hashCode() & Integer.MAX_VALUE) % numPartitions;
    }
}

Mapper代码

package com.lagou.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与金额封装为一个orderBean
        bean.setOrderId(fields[0]);
        bean.setPrice(Double.parseDouble(fields[2]));
        //这里按每一bean个实例为一个key方便进行排序处理
        context.write(bean, NullWritable.get());
    }
}

Reduce代码

package com.lagou.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());
    }
}

OrderBean代码

package com.lagou.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());

        }
        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
                ;
    }
}

Driver代码

package com.lagou.mr.group;

import com.lagou.mr.wc.WordCountDriver;
import com.lagou.mr.wc.WordCountMapper;
import com.lagou.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\\out")); //指定结果数据输出路径

        //指定reducetask的数量,不要使用默认的一个,分区效果不明显
        job.setNumReduceTasks(3);
//        8. 提交作业
        final boolean flag = job.waitForCompletion(true);
        //jvm退出:正常退出0,非0值则是错误退出
        System.exit(flag ? 0 : 1);

    }
}

总结

Reduce中的注释
key:reduce方法的key注意是一组相同key的kv的第一个key作为传入reduce方法的key,因为我们已经指定了排序的规则按照金额降序排列,则第一个key就是金额最大的交易数据

value:一组相同key的kv对中v的集合,对于如何判断key是否相同,自定义对象是需要我们指定一个规则,这个规则通过Groupingcomaprator来指定

相比全区排序,主要内容为CustomGroupingComparator:重新指定分组。OrderBean:中使用二次排序

流程示意

map方法 用bean实例作为key传递
排序
按照订单id分组 因为map传递的 key为bean实例
因为一组相同key的kv的第一个key作为传入reduce方法的key 所以我们直接输出key即可
GroupMapper
bean中重写compareTo方法
CustomGroupingComparator
到达Reduce时已是分组排序过的数据
CustomPartitioner分区输出结果

注意:上图排版暂时不确定分组与排序的先后顺序,仅作为个人理解,不代表实际情况。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值