• Hadoop 学习笔记(十五)WritableComparable 排序


    1、基本介绍

    排序是MapReduce框架中最重要的操作之一。
    MapTask和ReduceTask均会对数据按照key进行排序。该操作属于Hadoop的默认行为。任何应用程序中的数据均会被排序,而不管逻辑上是否需要。默认排序是按照字典顺序排序,且实现该排序的方法是快速排序。
    对于MapTask,它会将处理的结果暂时放到环形缓冲区中,当环形缓冲区使用率达到一定阈值后,再对缓冲区中的数据进行一次快速排序,并将这些有序数据溢写到磁盘上,而当数据处理完毕后,它会对磁盘上所有文件进行归并排序。
    对于ReduceTask,它从每个MapTask上远程拷贝相应的数据文件,如果文件大小超过一定阈值,则溢写磁盘上,否则存储在内存中。如果磁盘上文件数目达到一定阈值,则进行一次归并排序以生成一个更大文件;如果内存中文件大小或者数目超过一定阈值,则进行一次合并后将数据溢写到磁盘上。当所有数据拷贝完毕后,ReduceTask统一对内存和磁盘上的所有数据进行一次归并排序。

    常用排序:

    • 部分排序 MapReduce根据输入记录的键对数据集排序。保证输出的每个文件内部有序。
    • 全排序 最终输出结果只有一个文件,且文件内部有序。实现方式是只设置一个ReduceTask。但该方法在处理大型文件时效率极低,因为一台机器处理所有文件,完全丧失了MapReduce所提供的并行架构。
    • 辅助排序 在Reduce端对key进行分组。应用于:在接收的key为bean对象时,想让一个或几个字段相同(全部字段比较不相同)的key进入到同一个reduce方法时,可以采用分组排序。
    • 二次排序 在自定义排序过程中,如果compareTo中的判断条件为两个即为二次排序。

    2、自定义排序WritableComparable 

    bean对象做为key传输,需要实现WritableComparable接口重写compareTo方法,就可以实现排序。

    @Override
    public int compareTo(FlowBean o) {
    
        int result;
            
        // 按照总流量大小,倒序排列
        if (sumFlow > bean.getSumFlow()) {
            result = -1;
        }else if (sumFlow < bean.getSumFlow()) {
            result = 1;
        }else {
            result = 0;
        }
    
        return result;
    }

    案例:对序列化案例的输出结果的总流量进行全排序;

    FlowBean.java

    public class FlowBean implements WritableComparable<FlowBean> {
    
        private long upFlow;
        private long downFlow;
        private long sumFlow;
    
        // 反序列化时,需要反射调用空参构造函数,所以必须有
        public FlowBean() {
            super();
        }
    
        public FlowBean(long upFlow, long downFlow) {
            super();
            this.upFlow = upFlow;
            this.downFlow = downFlow;
            this.sumFlow = upFlow + downFlow;
        }
    
        public void set(long upFlow, long downFlow) {
            this.upFlow = upFlow;
            this.downFlow = downFlow;
            this.sumFlow = upFlow + downFlow;
        }
    
        public long getSumFlow() {
            return sumFlow;
        }
    
        public void setSumFlow(long sumFlow) {
            this.sumFlow = sumFlow;
        }
    
        public long getUpFlow() {
            return upFlow;
        }
    
        public void setUpFlow(long upFlow) {
            this.upFlow = upFlow;
        }
    
        public long getDownFlow() {
            return downFlow;
        }
    
        public void setDownFlow(long downFlow) {
            this.downFlow = downFlow;
        }
    
        /**
         * 序列化方法
         * 
         * @param out
         * @throws IOException
         */
        @Override
        public void write(DataOutput out) throws IOException {
            out.writeLong(upFlow);
            out.writeLong(downFlow);
            out.writeLong(sumFlow);
        }
    
        /**
         * 反序列化方法 注意反序列化的顺序和序列化的顺序完全一致
         * 
         * @param in
         * @throws IOException
         */
        @Override
        public void readFields(DataInput in) throws IOException {
            upFlow = in.readLong();
            downFlow = in.readLong();
            sumFlow = in.readLong();
        }
    
        @Override
        public String toString() {
            return upFlow + "	" + downFlow + "	" + sumFlow;
        }
    
        @Override
        //排序逻辑
        public int compareTo(FlowBean bean) {
    
            int result;
    
            // 按照总流量大小,倒序排列
            if (sumFlow > bean.getSumFlow()) {
                result = -1;
            } else if (sumFlow < bean.getSumFlow()) {
                result = 1;
            } else {
                result = 0;
            }
    
            return result;
        }
    }
    View Code

    FlowBeanMapper.java

    public class FlowBeanMapper extends Mapper<LongWritable, Text, FlowBean, Text> {
    
        FlowBean bean = new FlowBean();
        Text v = new Text();
    
        @Override
        protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
    
            // 1 获取一行
            String line = value.toString();
    
            // 2 截取
            String[] fields = line.split("	");
    
            // 3 封装对象
            String phoneNbr = fields[0];
            long upFlow = Long.parseLong(fields[1]);
            long downFlow = Long.parseLong(fields[2]);
    
            bean.set(upFlow, downFlow);
            v.set(phoneNbr);
    
            // 4 输出
            context.write(bean, v);
        }
    }
    View Code

    FlowBeanReduce.java

    public class FlowBeanReduce extends Reducer<FlowBean, Text, Text, FlowBean> {
    
        @Override
        protected void reduce(FlowBean key, Iterable<Text> values, Context context)
                throws IOException, InterruptedException {
    
            // 循环输出,避免总流量相同情况
            for (Text text : values) {
                context.write(text, key);
            }
        }
    }
    View Code

    FlowBeanDriver.java

    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
    
            args = new String[] { "E:/input/output1", "E:/output1" };
    
            // 1 获取配置信息,或者job对象实例
            Configuration configuration = new Configuration();
            Job job = Job.getInstance(configuration);
    
            // 2 指定本程序的jar包所在的本地路径
            job.setJarByClass(FlowBeanDriver.class);
    
            // 3 指定本业务job要使用的mapper/Reducer业务类
            job.setMapperClass(FlowBeanMapper.class);
            job.setReducerClass(FlowBeanReduce.class);
    
            // 4 指定mapper输出数据的kv类型
            job.setMapOutputKeyClass(FlowBean.class);
            job.setMapOutputValueClass(Text.class);
      
            // 关联分区
            /*
             * job.setPartitionerClass(ProvPartios.class); job.setNumReduceTasks(5);//设置分区
             */
            // 5 指定最终输出的数据的kv类型
            job.setOutputKeyClass(Text.class);
            job.setOutputValueClass(FlowBean.class);
    
            // 6 指定job的输入原始文件所在目录
            FileInputFormat.setInputPaths(job, new Path(args[0]));
            FileOutputFormat.setOutputPath(job, new Path(args[1]));
    
            // 7 将job中配置的相关参数,以及job所用的java类所在的jar包, 提交给yarn去运行
            boolean result = job.waitForCompletion(true);
            System.exit(result ? 0 : 1);
        }
    View Code

    运行输出一下结果

    13509468723    7335    110349    117684
    13736230513    2481    24681    27162
    13956435636    132        1512    1644
    13846544121    264        0        264
  • 相关阅读:
    Java生成二维码连接
    Spring boot 整合 Elasticsearch
    Windows下安装ElasticSearch
    springcloud zookeeper+gateway
    idea中Springcloud同时运行多个模块、微服务
    【转】为什么我的DIV块前总有空隙?
    【转载】通过JSFL让Flash Professional CS4或CS5拥有批量FLA导出SVG的功能
    jQuery Mobile中的页面加载与跳转机制
    关于Conversion to Dalvik format failed with error 1错误
    带权重的随机输出数组中的元素
  • 原文地址:https://www.cnblogs.com/wdh01/p/13917289.html
Copyright © 2020-2023  润新知