MapRduce特性:二次排序

在学习二次排序的过程中,觉得还是很复杂的,写一篇博客分享一下。

一、什么是二次排序?

    二次排序就是对value值进行排序(本身value值是不会排序的)

二、例子分析

    需求:

       求1920-2020年100年间每年气温的最大值。

    问题分析:

        1、这100年的数据每年的气温数据量十分的大

        2、如果在每次在reduce里面对整个气温找最大值的话很消耗时间

    方案:

        为了解决上述分析中的问题,在reduce之前,将所有的气温数据按年份从小到大排列,同一年份中,温度由又大到小排列,这样的话,reduce只要拿同一组中的第一个数据,就得到了当年的最高温度。

    mapreduce流程图:


    详细分析整个过程:

        1、map从reader中读取数据。

package cn.hbmy.hdfs.mr.secondsorttemperature;

import org.apache.hadoop.io.IntWritable;
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 SecondSortTemWCMapper extends Mapper <IntWritable,IntWritable,Combokey,NullWritable>{
    @Override
    protected void map(IntWritable key, IntWritable value, Context context) throws IOException, InterruptedException {
        Combokey combokey = new Combokey();
        combokey.setYear(key.get());
        combokey.setTemp(value.get());
        context.write(combokey,NullWritable.get());
    }
}

        2、由于MapReduce中,只对key,排序不对value排序,为了能够对value进行排序,自己定义一个combokey,成员变量包含key和value,也就是上图中的需要解决地方4 此时K由combokey代替,value为null。下面给出combokey的定义类:

package cn.hbmy.hdfs.mr.secondsorttemperature;

import org.apache.hadoop.io.WritableComparable;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

/**
 * 不仅要有可比较的接口,还要实现串行化的接口
 * */
public class Combokey implements WritableComparable<Combokey> {
    private  int year;
    private int temp;

    public int getYear() {
        return year;
    }

    public int getTemp() {
        return temp;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public void setTemp(int temp) {
        this.temp = temp;
    }

    /**
     * 对key进行比较实现
     */
    public int compareTo(Combokey o) {
            int y0 = o.getYear();
            int t0 = o.getTemp() ;
            //年份相同(升序)
            if(year == y0){
            //气温降序
            return -(temp - t0) ;
            }
            else{
            return year - y0 ;
            }
    }

    /**
     * 串行化过程  将数据写到流中去
     *
     * 反串行化过程是将流读入
     */
    public void write(DataOutput out) throws IOException {
            //年份
            out.writeInt(year);
            //气温
            out.writeInt(temp);
    }

    public void readFields(DataInput in) throws IOException {
            year = in.readInt();
            temp = in.readInt();
    }
}


        3、现在输入partitioner()函数的是(Combokey,NullWritable)了,此时,我们就需要自己实现partitioner()函数,进行分区,也就是上面的需要解决地方1下面给出分区函数:

package cn.hbmy.hdfs.mr.secondsorttemperature;

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


/**
 *  自定义分区就是为了防止数据倾斜
 *  也就是说:防止所有的数据都进入了一个reducer里面去了
 *  解决数据倾斜的两种方式:
 *  1、自定义分区类,
 *  2、重新制定key值
 * */
public class YearPartitioner extends Partitioner<Combokey,NullWritable>{
    public int getPartition(Combokey combokey, NullWritable nullWritable, int part) {
        /**
         * 当数据特别的集中的时候,这样的分区会导致数据倾斜,这里只是为了做二次排序,简单处理
         * */
        int year = combokey.getYear();
        return year % part;
    }
}

        4、map端现在完成了,到了reduce端,对现在的数据需要排序,将所有的气温数据按年份从小到大排列,同一年份中,温度由又大到小排列,定义一个类继承WritableComparator类即可,也就是上图中需要解决地方2。如下代码

package cn.hbmy.hdfs.mr.secondsorttemperature;

import org.apache.hadoop.io.WritableComparable;
import org.apache.hadoop.io.WritableComparator;


/**
 * comparator
 * */
public class ComboKeyComparator extends WritableComparator {
    protected ComboKeyComparator() {
        super(Combokey.class, true);
    }
    public int compare(WritableComparable a, WritableComparable b) {k
        Combokey k1 = (Combokey) a;
        Combokey k2 = (Combokey) b;
        /**
         * 调用combokey重定义好了的方法,实现key升序,value降序排列
         * */
        return k1.compareTo(k2);
    }
}

        5、数据排序完了,对数据还需要分组,因为key相等的数据会进入同一个reduce进行化简,每一组key会分配一个reduce函数。如上图解决地方3,黄色的框框中,一个框框内的东西就是一个组。具体实现如下:

package cn.hbmy.hdfs.mr.secondsorttemperature;


import org.apache.hadoop.io.WritableComparable;
import org.apache.hadoop.io.WritableComparator;

/**
 * 自定义分区对比器,使得相同的一组key聚合在一起去。
 * 例如我们传进来的数据是
 * key          value
 * 1920-31       null
 * 1921-32       null
 * 1920-33       null
 * 1921-33       null
 * 默认的是相等的key会聚集在一起,但是我们吧年份和气温聚集在了一起,
 * 所以使得相同的年份,默认的方法不能聚合在一起了,我们自己要重新分组
 * */
public class YearGroupComparator extends WritableComparator {
    protected YearGroupComparator() {
        super(Combokey.class,true);
    }
    public int compare(WritableComparable a, WritableComparable b) {
        Combokey k1 = (Combokey)a;
        Combokey k2 = (Combokey)b;
        /**
         * 这个到底是怎么分组的,需要单步的调试,看这个返回的结果是什么
         * */
        System.out.println("k1.getYear() - k2.getYear()"+k1.getYear() +"  "+k2.getYear());
        System.out.println(k1.getYear() - k2.getYear());
        return k1.getYear() - k2.getYear();
    }
}

        6、分完组之后,进入reduce,取每一组中的第一个数据,这个数据就是这一年中气温的最大值。如上图每一个黄色框框中的第一条数据就是该年度的最大值。

package cn.hbmy.hdfs.mr.secondsorttemperature;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

import java.io.IOException;

/**
 * Reducer
 *
 * 每一组会分配一个reduce
 * */
public class SecondSortTemWCReduce extends Reducer<Combokey,NullWritable,IntWritable,IntWritable> {
    @Override
    protected void reduce(Combokey key, Iterable<NullWritable> values, Context context) throws IOException, InterruptedException {
        //进来第一个数据,我们就把这个数据写出去
         int year = key.getYear();
         int temp = key.getTemp();
         //也可以通过for循环将数据全部的遍历出来
        context.write(new IntWritable(year),new IntWritable(temp));
    }
}

         7、APP类中的代码实现。

package cn.hbmy.hdfs.mr.secondsorttemperature;

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.input.SequenceFileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;


/**
 * 项目的参数
 * /upload/wangwei /upload/wangwei/out\
 *
 * 在集群上面运行的命令
 * hadoop jar hdfs-1.0-SNAPSHOT.jar  cn.hbmy.hdfs.mr.AllSortTemWCApp /upload/in /upload/wangwei/out
 hadoop jar hdfs-1.0-SNAPSHOT.jar  cn.hbmy.hdfs.mr.AllSortTemWCApp /upload//out         File System Counters
 FILE: Number of bytes read=137097
 FILE: Number of bytes written=1491213
 FILE: Number of read operations=0
 FILE: Number of large read operations=0
 FILE: Number of write operations=0
 HDFS: Number of bytes read=510
 HDFS: Number of bytes written=113
 HDFS: Number of read operations=72
 HDFS: Number of large read operations=0
 HDFS: Number of write operations=18
 Map-Reduce Framework
 Map input records=9
 Map output records=18
 Map output bytes=167
 Map output materialized bytes=173
 Input split bytes=318
 Combine input records=18
 Combine output records=10
 Reduce input groups=8
 Reduce shuffle bytes=173
 Reduce input records=10
 Reduce output records=8
 Spilled Records=20
 Shuffled Maps =9
 Failed Shuffles=0
 Merged Map outputs=9
 GC time elapsed (ms)=94
 CPU time spent (ms)=0
 Physical memory (bytes) snapshot=0
 Virtual memory (bytes) snapshot=0
 Total committed heap usage (bytes)=1325260800
 Shuffle Errors
 BAD_ID=0
 CONNECTION=0
 IO_ERROR=0
 WRONG_LENGTH=0
 WRONG_MAP=0
 WRONG_REDUCE=0
 m
 mini1:7167:LocalJobRunner Map Task Executor #0:AllSortTemWCMapper@1049875=10
 mini1:7167:LocalJobRunner Map Task Executor #0:AllSortTemWCMapper@1619279=4
 mini1:7167:LocalJobRunner Map Task Executor #0:AllSortTemWCMapper@2772633=4
 File Input Format Counters
 Bytes Read=95
 File Output Format Counters
 Bytes Written=61
 r
 mini1:7167:LocalJobRunner Map Task Executor #0:AllSortTemWCReduce@1607603=2
 mini1:7167:LocalJobRunner Map Task Executor #0:AllSortTemWCReduce@1903189=3
 mini1:7167:LocalJobRunner Map Task Executor #0:AllSortTemWCReduce@2697133=2
 mini1:7167:LocalJobRunner Map Task Executor #0:AllSortTemWCReduce@2892504=2
 mini1:7167:LocalJobRunner Map Task Executor #0:AllSortTemWCReduce@8917400=1
 mini1:7167:pool-6-thread-1:AllSortTemWCReduce@12779848:reduce=3
 mini1:7167:pool-6-thread-1:AllSortTemWCReduce@22463614:reduce=2
 mini1:7167:pool-6-thread-1:AllSortTemWCReduce@23423693:reduce=3
 * */
public class SecondSortTemWCApp {
    public  static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
//        conf.set("fs.defaultFS","file:///");
        Job job = Job.getInstance(conf);
        //设置job的各种属性
        job.setJobName("AllSortTemWCApp");                         //作业名称
        job.setJarByClass(SecondSortTemWCApp.class);                  //搜索类
        job.setInputFormatClass(SequenceFileInputFormat.class);  //设置序列文件的输入格式

        FileInputFormat.addInputPath(job,new Path(args[0]));   //添加输入路径
        FileOutputFormat.setOutputPath(job,new Path(args[1])); //设置输出路径

        //设置最大切片数   根据系统的切片法则,一般都是128M,也就是一个block
//        FileInputFormat.setMaxInputSplitSize(job,13);
        //设置最小切片数
//        FileInputFormat.setMinInputSplitSize(job,1L);

        job.setMapperClass(SecondSortTemWCMapper.class);
//        设置自定义分区,根据年份分区
        job.setPartitionerClass(YearPartitioner.class);
//        设置排序器
        job.setSortComparatorClass(ComboKeyComparator.class);
//        设置分组对比器
        job.setGroupingComparatorClass(YearGroupComparator.class);
        job.setReducerClass(SecondSortTemWCReduce.class);
                                                          //map的个数取决于切片数
        job.setNumReduceTasks(3);                         //reduce的个数,一个输出结果   设置三个reduce个数
        job.setMapOutputKeyClass(Combokey.class);
        job.setMapOutputValueClass(NullWritable.class);
        job.setOutputKeyClass(IntWritable.class);
        job.setOutputValueClass(IntWritable.class);
        job.waitForCompletion(true);
    }
}

三、总结:

        本人也是开始接触hadoop,对这个里面的class类文件还不是很熟悉,因此贴出来了代码,代码中也有注释,如果有异议的地方,希望大家多多交流。

猜你喜欢

转载自blog.csdn.net/qq_35468937/article/details/80512554