Hadoop排序,从大的范围来说有两种排序,一种是按照key排序,一种是按照value排序。如果按照value排序,只需在map函数中将key和value对调,然后在reduce函数中在对调回去。从小范围来说排序又分成部分排序,全局排序,辅助排序(二次排序)等。本文介绍如何在Hadoop中实现全局排序。
 
全局排序,就是说在一个MapReduce程序产生的输出文件中,所有的结果都是按照某个策略进行排序的,例如降序还是升序。MapReduce只能保证一个分区内的数据是key有序的,一个分区对应一个reduce,因此只有一个reduce就保证了数据全局有序,但是这样又不能用到Hadoop集群的优势。
 
对于多个reduce如何保证数据的全局排序呢?通常的做法是按照key值分区,通过MapReduce的默认分区函数HashPartition将不同范围的key发送到不同的reduce处理,例如一个文件中有key值从1到10000的数据,我们使用两个分区,将1到5000的key发送到partition1,然后由reduce1处理,5001到10000的key发动到partition2然后由reduce2处理,reduce1中的key是按照1到5000的升序排序,reduce2中的key是按照5001到10000的升序排序,这样就保证了整个MapReduce程序的全局排序。但是这样做有两个缺点:
1、当数据量大时会出现OOM。
2、会出现数据倾斜。
 
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 TotalSortMap extends Mapper<Text, Text, Text, IntWritable> {
@Override
protected void map(Text key, Text value,
Context context) throws IOException, InterruptedException {
context.write(key, new IntWritable(Integer.parseInt(key.toString())));
}
}
Reduce类:
 public class TotalSortReduce extends Reducer<Text, IntWritable, IntWritable, NullWritable> {
@Override
protected void reduce(Text key, Iterable<IntWritable> values,
Context context) throws IOException, InterruptedException {
for (IntWritable value : values)
context.write(value, NullWritable.get());
}
}

入口类:

 public class TotalSort extends Configured implements Tool{

     //实现一个Kye比较器,用于比较两个key的大小,将key由字符串转化为Integer,然后进行比较。
public static class KeyComparator extends WritableComparator {
protected KeyComparator() {
super(Text.class, true);
} @Override
public int compare(WritableComparable writableComparable1, WritableComparable writableComparable2) {
int num1 = Integer.parseInt(writableComparable1.toString());
int num2 = Integer.parseInt(writableComparable2.toString()); return num1 - num2;
}
}
@Override
public int run(String[] args) throws Exception {
Configuration conf = new Configuration();
conf.set("mapreduce.totalorderpartitioner.naturalorder", "false");
Job job = Job.getInstance(conf, "Total Sort app");
job.setJarByClass(TotalSort.class); //设置读取文件的路径,都是从HDFS中读取。读取文件路径从脚本文件中传进来
FileInputFormat.addInputPath(job,new Path(args[0]));
//设置mapreduce程序的输出路径,MapReduce的结果都是输入到文件中
FileOutputFormat.setOutputPath(job,new Path(args[1]));
job.setInputFormatClass(KeyValueTextInputFormat.class);
//设置比较器,用于比较数据的大小,然后按顺序排序,该例子主要用于比较两个key的大小
job.setSortComparatorClass(KeyComparator.class);
job.setNumReduceTasks(3);//设置reduce数量 job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
job.setOutputKeyClass(IntWritable.class);
job.setOutputValueClass(NullWritable.class); //设置保存partitions文件的路径
TotalOrderPartitioner.setPartitionFile(job.getConfiguration(), new Path(args[2]));
//key值采样,0.01是采样率,
InputSampler.Sampler<Text, Text> sampler = new InputSampler.RandomSampler<>(0.01, 1000, 100);
//将采样数据写入到分区文件中
InputSampler.writePartitionFile(job, sampler); job.setMapperClass(TotalSortMap.class);
job.setReducerClass(TotalSortReduce.class);
//设置分区类。
job.setPartitionerClass(TotalOrderPartitioner.class);
return job.waitForCompletion(true) ? 0 : 1;
}
public static void main(String[] args)throws Exception{ int exitCode = ToolRunner.run(new TotalSort(), args);
System.exit(exitCode);
}
}
生成测试数据的代码如下:
 #!/bin/bash
do
for k in $(seq )
echo $RANDOM;
done
将上面代码保存成create_data.sh,然后执行
sh create_data.sh > test_data.txt
会生成一个test_data.txt的文本文件,文本中的内容是一行一个随机数字
将test_data.txt上传到HDFS中:
hadoop fs -put test_data.txt /data/
将上面的实现全局排序的代码打成一个jar包,然后通过shell文件执行。
执行MapReduce代码的脚本如下:
 /usr/local/src/hadoop-2.6./bin/hadoop jar TotalSort.jar \
hdfs://hadoop-master:8020/data/test_data1.txt \
hdfs://hadoop-master:8020/total_sort_output \
hdfs://hadoop-master:8020/total_sort_partitions
看下运行结果,我们只需要看part-r-00000的尾10行和part-r-00001的头10行数据,只要它们收尾相接就证明是全局有序的:

下面有几个坑要注意,大家不要踩:

  1. 数据的输入类型必须使用KeyValueTextInputFormat类而不是TextInputFormat类,因为hadoop采样器是对key值采样,而TextInputFormat的key是位置偏移量,value存放的是每行的输入数据,对该key采样没有任何意义。KeyValueTextInputFormat的key存放的是输入数据,对key采样才能更好的划分分区。用法:

    job.setInputFormatClass(KeyValueTextInputFormat.class);
  2. 使用代码conf.set("mapreduce.totalorderpartitioner.naturalorder", "false")设置分区的排序策略,否则是每个分区内有序,而不是全局有序。

  3. 采样器只能是Text,Text类型:InputSampler.Sampler<Text, Text>,否则会报Exception in thread "main" java.io.IOException: wrong key class: org.apache.hadoop.io.Text is not class org.apache.hadoop.io.LongWritable这个错误。

  4. job.setMapOutputKeyClass(Text.class)和job.setMapOutputValueClass(IntWritable.class)这两行代码必须在InputSampler.Sampler<Text, Text> sampler = new InputSampler.RandomSampler<>(0.01, 1000, 100);这行代码之前调用,否则会报Exception in thread "main" java.io.IOException: wrong key class: org.apache.hadoop.io.Text is not class org.apache.hadoop.io.LongWritable错误。
  5. 调用setSortComparatorClass方法设置排序类,对key进行排序。job.setSortComparatorClass(KeyComparator.class);类似例子中的KeyComparator类。否则是按照字典序进行排序。MapReduce默认输出的key是字符类型时,默认是按照字典序排序。

一起学Hadoop——TotalOrderPartitioner类实现全局排序的更多相关文章

  1. Hadoop对文本文件的快速全局排序

    一.背景 Hadoop中实现了用于全局排序的InputSampler类和TotalOrderPartitioner类,调用示例是org.apache.hadoop.examples.Sort. 但是当 ...

  2. MapReduce TotalOrderPartitioner 全局排序

    我们知道Mapreduce框架在feed数据给reducer之前会对map output key排序,这种排序机制保证了每一个reducer局部有序,hadoop 默认的partitioner是Has ...

  3. 三种方法实现Hadoop(MapReduce)全局排序(1)

    我们可能会有些需求要求MapReduce的输出全局有序,这里说的有序是指Key全局有序.但是我们知道,MapReduce默认只是保证同一个分区内的Key是有序的,但是不保证全局有序.基于此,本文提供三 ...

  4. 一起学Hadoop——使用自定义Partition实现hadoop部分排序

    排序在很多业务场景都要用到,今天本文介绍如何借助于自定义Partition类实现hadoop部分排序.本文还是使用java和python实现排序代码. 1.部分排序. 部分排序就是在每个文件中都是有序 ...

  5. Hadoop的partitioner、全排序

    按数值排序 示例:按气温字段对天气数据集排序问题:不能将气温视为Text对象并以字典顺序排序正统做法:用顺序文件存储数据,其IntWritable键代表气温,其Text值就是数据行常用简单做法:首先, ...

  6. MapReduce怎么优雅地实现全局排序

    思考 想到全局排序,是否第一想到的是,从map端收集数据,shuffle到reduce来,设置一个reduce,再对reduce中的数据排序,显然这样和单机器并没有什么区别,要知道mapreduce框 ...

  7. [大牛翻译系列]Hadoop(6)MapReduce 排序:总排序(Total order sorting)

    4.2.2 总排序(Total order sorting) 有的时候需要将作业的的所有输出进行总排序,使各个输出之间的结果是有序的.有以下实例: 如果要得到某个网站中最受欢迎的网址(URL),就需要 ...

  8. Mapreduce的排序(全局排序、分区加排序、Combiner优化)

    一.MR排序的分类 1.部分排序:MR会根据自己输出记录的KV对数据进行排序,保证输出到每一个文件内存都是经过排序的: 2.全局排序: 3.辅助排序:再第一次排序后经过分区再排序一次: 4.二次排序: ...

  9. 大数据mapreduce全局排序top-N之python实现

    a.txt.b.txt文件如下: a.txt hadoop hadoop hadoop hadoop hadoop hadoop hadoop hadoop hadoop hadoop hadoop ...

随机推荐

  1. 微信小程序-聊天列表-角标

    <div class="list-body" bindtap='openChat' data-Obj='{{oitem}}'> <!-- 头像 --> &l ...

  2. <转载>ford-fulkerson算法2

    原文链接https://www.cnblogs.com/luweiseu/archive/2012/07/14/2591573.html 作者:wlu 7. 网络流算法--Ford-Fulkerson ...

  3. [转]PHP中file_put_contents追加和换行

    在PHP的一些应用中需要写日志或者记录一些信息,这样的话. 可以使用fopen(),fwrite()以及 fclose()这些进行操作. 也可以简单的使用file_get_contents()和fil ...

  4. 面向对象(metaclass继承高级用法)

    方法一:# class MyType(type):# def __init__(self,*args,**kwargs):# print('132')# super(MyType,self).__in ...

  5. 02 编程语言、python变量

    一 编程语言介绍(***) 1.1机器语言:直接用计算机能理解的二进制指令编写程序,直接控制硬件 优点:执行效率高,跨平台性低 缺点:开发效率低 1.2汇编语言:用英文标签取代二进制指令编写程序,本质 ...

  6. python多线程中锁的概念

    1 2 3 4 5 6 7 8 mutex = threading.Lock() #锁的使用 #创建锁 mutex = threading.Lock() #锁定 mutex.acquire([time ...

  7. bzoj4025 二分图 [分治,并查集]

    传送门 思路 是二分图的充要条件:图没有奇环. 考虑按时间分治,用可撤销并查集维护点到根的距离. 仍然可以用一个小trick把两点连边变成根连边,可以看这里. 每次连边时若不连通则连上,否则判一下有没 ...

  8. Selenium WebDriver中鼠标事件

    鼠标点击操作  鼠标点击事件有以下几种类型:  清单 1. 鼠标左键点击   Actions action = new Actions(driver);action.click();// 鼠标左键在当 ...

  9. LeetCode(3):无重复字符的最大子串

    本内容是LeetCode第三道题目:无重复字符的最大子串 # -*- coding: utf-8 -*- """ Created on Sun Mar 10 20:14: ...

  10. PDF裁剪页面,PDF怎么裁剪页面的方法

    PDF文件要怎么裁剪页面呢,是不是有很多的小伙们想知道呢,当打开一个PDF文件的时候如果一个页面中有很多的空白页面就会影响文件的美观与使用,今天小编就为大家分享一下小编的裁剪页面的方法. 操作软件:迅 ...