hadoop2.2编程:mapreduce编程之二次排序
mr自带的例子中的源码SecondarySort,我重新写了一下,基本没变。
这个例子中定义的map和reduce如下,关键是它对输入输出类型的定义:(java泛型编程)
public static class Map extends Mapper<LongWritable, Text, IntPair, IntWritable>
public static class Reduce extends Reducer<IntPair, NullWritable, IntWritable, IntWritable>
1 首先说一下工作原理:
在map阶段,使用job.setInputFormatClass定义的InputFormat将输入的数据集分割成小数据块splites,同时InputFormat提供一个RecordReder的实现。本例子中使用的是TextInputFormat,他提供的RecordReder会将文本的一行的行号作为key,这一行的文本作为value。这就是自定义Map的输入是<LongWritable, Text>的原因。然后调用自定义Map的map方法,将一个个<LongWritable, Text>对输入给Map的map方法。注意输出应该符合自定义Map中定义的输出<IntPair, IntWritable>。最终是生成一个List<IntPair, IntWritable>。在map阶段的最后,会先调用job.setPartitionerClass对这个List进行分区,每个分区映射到一个reducer。每个分区内又调用job.setSortComparatorClass设置的key比较函数类排序。可以看到,这本身就是一个二次排序。如果没有通过job.setSortComparatorClass设置key比较函数类,则使用key的实现的compareTo方法。在第一个例子中,使用了IntPair实现的compareTo方法,而在下一个例子中,专门定义了key比较函数类。
在reduce阶段,reducer接收到所有映射到这个reducer的map输出后,也是会调用job.setSortComparatorClass设置的key比较函数类对所有数据对排序。然后开始构造一个key对应的value迭代器。这时就要用到分组,使用jobjob.setGroupingComparatorClass设置的分组函数类。只要这个比较器比较的两个key相同,他们就属于同一个组,它们的value放在一个value迭代器,而这个迭代器的key使用属于同一个组的所有key的第一个key。最后就是进入Reducer的reduce方法,reduce方法的输入是所有的(key和它的value迭代器)。同样注意输入与输出的类型必须与自定义的Reducer中声明的一致。
2 二次排序就是首先按照第一字段排序,然后再对第一字段相同的行按照第二字段排序,注意不能破坏第一次排序的结果 。例如
输入文件
20 21
50 51
50 52
50 53
50 54
60 51
60 53
60 52
60 56
60 57
70 58
60 61
70 54
70 55
70 56
70 57
70 58
1 2
3 4
5 6
7 82
203 21
50 512
50 522
50 53
530 54
40 511
20 53
20 522
60 56
60 57
740 58
63 61
730 54
71 55
71 56
73 57
74 58
12 211
31 42
50 62
7 8
输出:(注意需要分割线)
------------------------------------------------
1 2
------------------------------------------------
3 4
------------------------------------------------
5 6
------------------------------------------------
7 8
7 82
------------------------------------------------
12 211
------------------------------------------------
20 21
20 53
20 522
------------------------------------------------
31 42
------------------------------------------------
40 511
------------------------------------------------
50 51
50 52
50 53
50 53
50 54
50 62
50 512
50 522
------------------------------------------------
60 51
60 52
60 53
60 56
60 56
60 57
60 57
60 61
------------------------------------------------
63 61
------------------------------------------------
70 54
70 55
70 56
70 57
70 58
70 58
------------------------------------------------
71 55
71 56
------------------------------------------------
73 57
------------------------------------------------
74 58
------------------------------------------------
203 21
------------------------------------------------
530 54
------------------------------------------------
730 54
------------------------------------------------
740 58
3 具体步骤:
(1)自定义key
在mr中,所有的key是需要被比较和排序的,并且是二次,先根据partitione,再根据大小。而本例中也是要比较两次。先按照第一字段排序,然后再对第一字段相同的按照第二字段排序。根据这一点,我们可以构造一个复合类IntPair,他有两个字段,先利用分区对第一字段排序,再利用分区内的比较对第二字段排序。
所有自定义的key应该实现接口WritableComparable,因为是可序列的并且可比较的。并重载方法:
- public static class FirstPartitioner extends Partitioner<IntPair,IntWritable>
在job中使用setPartitionerClasss设置Partitioner。
(2.2)key比较函数类。这是key的第二次比较。这是一个比较器,需要继承WritableComparator。
- public static class GroupingComparator extends WritableComparator
分组函数类也必须有一个构造函数,并且重载 public int compare(WritableComparable w1, WritableComparable w2)
分组函数类的另一种方法是实现接口RawComparator。
在job中使用setGroupingComparatorClass设置分组函数类。
另外注意的是,如果reduce的输入与输出不是同一种类型,则不要定义Combiner也使用reduce,因为Combiner的输出是reduce的输入。除非重新定义一个Combiner。
3 代码。
这个例子中没有使用key比较函数类,而是使用key的实现的compareTo方法。
package secondarySort; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.util.StringTokenizer; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.io.WritableComparable; import org.apache.hadoop.io.WritableComparator; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.Mapper; import org.apache.hadoop.mapreduce.Partitioner; import org.apache.hadoop.mapreduce.Reducer; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.mapreduce.lib.input.TextInputFormat; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat; public class SecondarySort { //自己定义的key类应该实现WritableComparable接口 public static class IntPair implements WritableComparable<IntPair> { int first; int second; /** * Set the left and right values. */ public void set(int left, int right) { first = left; second = right; } public int getFirst() { return first; } public int getSecond() { return second; } @Override //反序列化,从流中的二进制转换成IntPair public void readFields(DataInput in) throws IOException { // TODO Auto-generated method stub first = in.readInt(); second = in.readInt(); } @Override //序列化,将IntPair转化成使用流传送的二进制 public void write(DataOutput out) throws IOException { // TODO Auto-generated method stub out.writeInt(first); out.writeInt(second); } @Override //key的比较 public int compareTo(IntPair o) { // TODO Auto-generated method stub if (first != o.first) { return first < o.first ? -1 : 1; } else if (second != o.second) { return second < o.second ? -1 : 1; } else { return 0; } } //新定义类应该重写的两个方法 @Override //The hashCode() method is used by the HashPartitioner (the default partitioner in MapReduce) public int hashCode() { return first * 157 + second; } @Override public boolean equals(Object right) { if (right == null) return false; if (this == right) return true; if (right instanceof IntPair) { IntPair r = (IntPair) right; return r.first == first && r.second == second; } else { return false; } } } /** * 分区函数类。根据first确定Partition。 */ public static class FirstPartitioner extends Partitioner<IntPair,IntWritable>{ @Override public int getPartition(IntPair key, IntWritable value, int numPartitions) { return Math.abs(key.getFirst() * 127) % numPartitions; } } /** * 分组函数类。只要first相同就属于同一个组。 */ /*//第一种方法,实现接口RawComparator public static class GroupingComparator implements RawComparator<IntPair> { @Override public int compare(IntPair o1, IntPair o2) { int l = o1.getFirst(); int r = o2.getFirst(); return l == r ? 0 : (l < r ? -1 : 1); } @Override //一个字节一个字节的比,直到找到一个不相同的字节,然后比这个字节的大小作为两个字节流的大小比较结果。 public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2){ // TODO Auto-generated method stub return WritableComparator.compareBytes(b1, s1, Integer.SIZE/8, b2, s2, Integer.SIZE/8); } }*/ //第二种方法,继承WritableComparator public static class GroupingComparator extends WritableComparator { protected GroupingComparator() { super(IntPair.class, true); } @Override //Compare two WritableComparables. public int compare(WritableComparable w1, WritableComparable w2) { IntPair ip1 = (IntPair) w1; IntPair ip2 = (IntPair) w2; int l = ip1.getFirst(); int r = ip2.getFirst(); return l == r ? 0 : (l < r ? -1 : 1); } } // 自定义map public static class Map extends Mapper<LongWritable, Text, IntPair, IntWritable> { private final IntPair intkey = new IntPair(); private final IntWritable intvalue = new IntWritable(); public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String line = value.toString(); StringTokenizer tokenizer = new StringTokenizer(line); int left = 0; int right = 0; if (tokenizer.hasMoreTokens()) { left = Integer.parseInt(tokenizer.nextToken()); if (tokenizer.hasMoreTokens()) right = Integer.parseInt(tokenizer.nextToken()); intkey.set(left, right); intvalue.set(right); context.write(intkey, intvalue); } } } // 自定义reduce // public static class Reduce extends Reducer<IntPair, IntWritable, Text, IntWritable> { private final Text left = new Text(); private static final Text SEPARATOR = new Text("------------------------------------------------"); public void reduce(IntPair key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException { context.write(SEPARATOR, null); left.set(Integer.toString(key.getFirst())); for (IntWritable val : values) { context.write(left, val); } } } /** * @param args */ public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException { // TODO Auto-generated method stub // 读取hadoop配置 Configuration conf = new Configuration(); // 实例化一道作业 Job job = new Job(conf, "secondarysort"); job.setJarByClass(SecondarySort.class); // Mapper类型 job.setMapperClass(Map.class); // 不再需要Combiner类型,因为Combiner的输出类型<Text, IntWritable>对Reduce的输入类型<IntPair, IntWritable>不适用 //job.setCombinerClass(Reduce.class); // Reducer类型 job.setReducerClass(Reduce.class); // 分区函数 job.setPartitionerClass(FirstPartitioner.class); // 分组函数 job.setGroupingComparatorClass(GroupingComparator.class); // map 输出Key的类型 job.setMapOutputKeyClass(IntPair.class); // map输出Value的类型 job.setMapOutputValueClass(IntWritable.class); // rduce输出Key的类型,是Text,因为使用的OutputFormatClass是TextOutputFormat job.setOutputKeyClass(Text.class); // rduce输出Value的类型 job.setOutputValueClass(IntWritable.class); // 将输入的数据集分割成小数据块splites,同时提供一个RecordReder的实现。 job.setInputFormatClass(TextInputFormat.class); // 提供一个RecordWriter的实现,负责数据输出。 job.setOutputFormatClass(TextOutputFormat.class); // 输入hdfs路径 FileInputFormat.setInputPaths(job, new Path(args[0])); // 输出hdfs路径 FileOutputFormat.setOutputPath(job, new Path(args[1])); // 提交job System.exit(job.waitForCompletion(true) ? 0 : 1); } }
hadoop2.2编程:mapreduce编程之二次排序的更多相关文章
- 实验六 MapReduce实验:二次排序
实验指导: 6.1 实验目的基于MapReduce思想,编写SecondarySort程序. 6.2 实验要求要能理解MapReduce编程思想,会编写MapReduce版本二次排序程序,然后将其执行 ...
- MapReduce二次排序
默认情况下,Map 输出的结果会对 Key 进行默认的排序,但是有时候需要对 Key 排序的同时再对 Value 进行排序,这时候就要用到二次排序了.下面让我们来介绍一下什么是二次排序. 二次排序原理 ...
- mapreduce二次排序详解
什么是二次排序 待排序的数据具有多个字段,首先对第一个字段排序,再对第一字段相同的行按照第二字段排序,第二次排序不破坏第一次排序的结果,这个过程就称为二次排序. 如何在mapreduce中实现二次排序 ...
- MapReduce 二次排序
默认情况下,Map 输出的结果会对 Key 进行默认的排序,但是有时候需要对 Key 排序的同时再对 Value 进行排序,这时候就要用到二次排序了.下面让我们来介绍一下什么是二次排序. 二次排序原理 ...
- 分别使用Hadoop和Spark实现二次排序
零.序(注意本部分与标题无太大关系,可直接调至第一部分) 既然没用为啥会有序?原因不想再开一篇文章,来抒发点什么感想或者计划了,就在这里写点好了: 前些日子买了几本书,打算学习和研究大数据方面的知识, ...
- 《Data-Intensive Text Processing with mapReduce》读书笔记之二:mapreduce编程、框架及运行
搜狐视频的屌丝男士第二季大结局了,惊现波多野老师,怀揣着无比鸡冻的心情啊,可惜随着剧情的推进发展,并没有出现期待中的屌丝奇遇,大鹏还是没敢冲破尺度的界线.想百度些种子吧,又不想让电脑留下污点证据,要知 ...
- hadoop2.2编程:使用MapReduce编程实例(转)
原文链接:http://www.cnblogs.com/xia520pi/archive/2012/06/04/2534533.html 从网上搜到的一篇hadoop的编程实例,对于初学者真是帮助太大 ...
- 【MapReduce】二、MapReduce编程模型
通过前面的实例,可以基本了解MapReduce对于少量输入数据是如何工作的,但是MapReduce主要用于面向大规模数据集的并行计算.所以,还需要重点了解MapReduce的并行编程模型和运行机制 ...
- mapreduce编程(一)-二次排序
转自:http://blog.csdn.net/heyutao007/article/details/5890103 mr自带的例子中的源码SecondarySort,我重新写了一下,基本没变. 这个 ...
随机推荐
- python中关于正则表达式二
2.2 反向引用 \1, \2... 表达式在匹配时,表达式引擎会将小括号 "( )" 包含的表达式所匹配到的字符串记录下来.在获取匹配结果的时候,小括号包含的表达式所匹配到的字符 ...
- 2.redis.3.2 下载,安装、配置、使用 - 2
上篇简单介绍了 下载,安装,测试,现在直接使用了,看结果 使用的redis服务便是,上篇临时搭建的简易服务,,注意,说的是简易,因为它只是一个单点的“玩具”: 临时在项目登录的时候模拟了一下,这里使用 ...
- mysql的分区技术(建立分区)
-- mysql建立表分区,使用range方法建立: create table t_range( id int(11), money int(11) unsigned not null, date d ...
- Oracle hextoraw和rawtohex
Oracle hextoraw和rawtohex [日期:2012-07-17] 来源:Linux社区 作者:adrain_001 [字体:大 中 小] HEXTORAW 语法: HEXT ...
- python 在调用时计算默认值
大家都知道python的默认值是在函数定义时计算出来的, 也就是说默认值只会计算一次, 之后函数调用时, 如果参数没有给出,同一个值会赋值给变量, 这会导致, 如果我们想要一个list默认值, 新手通 ...
- apache常用工作模式的区别
apache常用工作模式有两种 worker模式1.线程模式2.占用资源少3.稳定性略差4.并发大 prefork模式1.进程模式2.占用资源多3.稳定4.并发一般 来自为知笔记(Wiz)
- mysql 主从 Got fatal error 1236 from master when reading data from binary log: 'Could not find first 错误
本地MySQL环境,是两台MySQL做M-M复制.今天发现错误信息: mysql 5.5.28-log> show slave status\G ************************ ...
- Makefile隐含规则和用到的默认变量
如果要使用隐含规则生成你需要的目标,你所需要做的就是不要写出这个目标的规则.那么,make会试图去自动推导产生这个目标的规则和命令,如果make可以自动推导生成这个目标的规则和命令,那么这个行为就是隐 ...
- wamp5 忘记mysql root密码 重置方法
wamp5比较恶心,忘记mysql root密码了,重新安装都没用,网上找了个文章可以修改root的密码. http://www.kuqin.com/database/20080306/4249.ht ...
- open()函数
STDOUT_FILENO 1 标准输入 STDIN_FILENO 0 标准输出 STDERR_FILENO 2 标准错误 在/proc目 ...