MapReduce排序总结

【1】Hadoop默认的排序算法,只会针对key值进行排序,按照字典顺序排序

【2】二次排序,在一个数据文件中,首先按照key排序。在key相同的情况下,再按照value大小排序。难度在于要同时参考两列的数据,可以将一行中的两列值封装到bean中。实现WritableComparable接口,重写compareTo进行排序,指定比较规则,实现二次排序,具体可参见博客

【3】全局排序
1、使用一个Reducer
优点:实现简单
缺点:没有利用分布式

2、重写Partioner类
通过重写Partition类,把key在一个范围内的发往一个固定的Reducer,这样在一个Reducer内key是全排序的,在Reducer之间按照序号也是排好序的。比如key代表的是一个年龄,可以把数据输出到10个Reducer。1-10岁之间发往第0个Reducer,11-20发往第2个Reducer,以此类推。但是这样做有两个缺点:

  1. 当数据量大时会出现OOM(内存用完了)
  2. 会出现数据倾斜

3、 TotalOrderPartitioner类
Hadoop提供TotalOrderPartitioner类用于实现全局排序的功能,并且解决了OOM和数据倾斜的问题。TotalOrderPartitioner类提供了数据采样器,对key值进行部分采样,然后按照采样结果寻找key值的最佳分割点,将key值均匀的分配到不同的分区中。TotalOrderPartitioner 类提供了三个采样器,分别是:

  • SplitSampler 分片采样器,从数据分片中采样数据,该采样器不适合已经排好序的数据
  • RandomSampler随机采样器,按照设置好的采样率从一个数据集中采样,是一个优秀的通配采样器
  • IntervalSampler间隔采样机,以固定的间隔从分片中采样数据,对于已经排好序的数据效果非常好

三个采样器都实现了K[] getSample(InputFormat<K,V> inf, Job job)方法,该方法返回的是K[]数组,数组中存放的是根据采样结果返回的key值,即分隔点,MapRdeuce就是根据K[]数组的长度N生成N-1个分区partition数量,然后按照分割点的范围将对应的数据发送到对应的分区中。TotalOrderPartitioner类实现全局排序的功能。代码如下:

Map类:
public class MaxTempMapper extends Mapper<IntWritable, IntWritable, IntWritable, IntWritable>{
    protected void map(IntWritable key, IntWritable value, Context context) throws IOException, InterruptedException {
     /*   String line=value.toString();
        String arr[]=line.split(" ");*/
        context.write(key,value);
    }
}

 
Reduce类:
public class MaxTempReducer extends Reducer<IntWritable, IntWritable, IntWritable, IntWritable>{
    /**
     * reduce
     */
    protected void reduce(IntWritable key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
        int max=Integer.MIN_VALUE;
        for (IntWritable iw:values) {
            max=max>iw.get()?max:iw.get();
        }
        context.write(key,new IntWritable(max));
    }
}

App类:
public class MaxTemp {
    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("MaxTempApp");                        //作业名称
        job.setJarByClass(MaxTemp.class);                 //搜索类
        job.setInputFormatClass(SequenceFileInputFormat.class); //设置输入格式
        //添加输入路径
        FileInputFormat.addInputPath(job,new Path("F:\\mr\\seq"));
        //设置输出路径
        FileOutputFormat.setOutputPath(job,new Path("F:\\mr\\seq\\out"));
        job.setMapperClass(MaxTempMapper.class);             //mapper类
        job.setReducerClass(MaxTempReducer.class);           //reducer类
        job.setNumReduceTasks(3);                       //reduce个数
        job.setMapOutputKeyClass(IntWritable.class);           //
        job.setMapOutputValueClass(IntWritable.class);  //
        job.setOutputKeyClass(IntWritable.class);
        job.setOutputValueClass(IntWritable.class);     //
        //设置全排序分区类S
        job.setPartitionerClass(TotalOrderPartitioner.class);
        //创建随机采样器
        /**
         * freq:key被选中的概率
         * numSampales 抽取样本的总数
         * maxSplitsSampled 最大采样切片数
         */
        InputSampler.Sampler<IntWritable,IntWritable> sampler=
                new InputSampler.RandomSampler<IntWritable, IntWritable>(0.1,50,10);
        TotalOrderPartitioner.setPartitionFile(job.getConfiguration(),new Path("file:///f:/mr/par.lst"));
        //将sample数据写入分区文件中
        InputSampler.writePartitionFile(job,sampler);
        job.waitForCompletion(true);
    }
}

会有全局的排序文件输出。

发布了107 篇原创文章 · 获赞 19 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/ThreeAspects/article/details/103629231
今日推荐