利用hadoop mapreduce 做数据排序

  我们的需求是想统计一个文件中用IK分词后每个词出现的次数,然后按照出现的次数降序排列。也就是高频词统计。
由于hadoop在reduce之后就不能对结果做什么了,所以只能分为两个job完成,第一个job统计次数,第二个job对第一个job的结果排序。 第一个job的就是hadoop最简单的例子countwords,我要说的是用hadoop对结果排序。 假设第一个job的结果输出如下:
part-r-0000文件内容:

a   5
b   4
c   74
d   78
e   1
r   64
f    4
要做的就是按照每个词出现的次数降序排列。
1 **********************************分割线*****************************************

首先可能会出现这样的问题:

1.可能上一个job为多个reduce,也就是会产生多个结果文件,因为一个reduce就会生成一个结果文件,结果存放在上一个job输出目录下类似part-r-00的文件里。

2.需要排序的文件内容很大,所以需要考虑多个reduce的情况。

1 *********************************分割线*******************************

怎么去设计mapreduce

1.在map阶段按照行读取文本,然后调用map方法时把上一个job的结果颠倒,也就是map后结果应该是这样的

1 5    a
2 4    b
3 74    c
4 ................
5 .........................
6 4    f

2.然后map后,hadoop会对结果进行分组,这时结果就会变成

(5:a)
(4:b,f)
(74:c)
3.然后按照reduce数目的大小自定义分区函数,让结果形成多个区间,比如我认为大于50的应该在一个区间,一共3个reduce,那么最后的数据应该是三个区间,大于50的直接分到第一个分区0,25到50之间的分到第二个分区1,小于25的分到第三个分区2.因为分区数和reduce数是相同的,所以不同的分区对应不同的reduce,因为分区是从0开始的,分区是0的会分到第一个reduce处理,分区是1的会分到第2个reduce处理,依次类推。并且reduce对应着输出文件,所以,第一个reduce生成的文件就会是part-r-0000,第二个reduce对应的生成文件就会是part-r-0001,依次类推,所以reduce处理时只需要把key和value再倒过来直接输出。这样最后就会让形成数目最大的字符串就会在第一个生成文件里,排好的序就会文件命的顺序。 代码如下:
1 *******************************分割线*****************************************

map:

01 /**
02  * 把上一个mapreduce的结果的key和value颠倒,调到后就可以按照key排序了。
03  *
04  * @author zhangdonghao
05  *
06  */
07     public class SortIntValueMapper extends
08     Mapper<LongWritable, Text, IntWritable, Text> {
09 private final static IntWritable wordCount = new IntWritable(1);
10  
11 private Text word = new Text();
12  
13 public SortIntValueMapper() {
14     super();
15 }
16  
17 @Override
18 public void map(LongWritable key, Text value, Context context)
19         throws IOException, InterruptedException {
20  
21     StringTokenizer tokenizer = new StringTokenizer(value.toString());
22     while (tokenizer.hasMoreTokens()) {
23         word.set(tokenizer.nextToken().trim());
24         wordCount.set(Integer.valueOf(tokenizer.nextToken().trim()));
25         context.write(wordCount, word);
26     }
27 }
28 }

reudce:

01 /**
02  * 把key和value颠倒过来输出
03  * @author zhangdonghao
04  *
05  */
06     public class SortIntValueReduce extends
07     Reducer<IntWritable, Text, Text, IntWritable> {
08  
09 private Text result = new Text();
10  
11 @Override
12 public void reduce(IntWritable key, Iterable<Text> values, Context context)
13         throws IOException, InterruptedException {
14     for (Text val : values) {
15         result.set(val.toString());
16         context.write(result, key);
17     }
18  
19 }
20 }

Partitioner:

01 /**
02  * 按照key的大小来划分区间,当然,key 是 int值
03  *
04  * @author zhangdonghao
05  *
06  */
07 public class KeySectionPartitioner<K, V> extends Partitioner<K, V> {
08  
09 public KeySectionPartitioner() {
10 }
11  
12 @Override
13 public int getPartition(K key, V value, int numReduceTasks) {
14     /**
15      * int值的hashcode还是自己本身的数值
16      */
17     //这里我认为大于maxValue的就应该在第一个分区
18     int maxValue = 50;
19     int keySection = 0;
20     // 只有传过来的key值大于maxValue 并且numReduceTasks比如大于1个才需要分区,否则直接返回0
21     if (numReduceTasks > 1 && key.hashCode() < maxValue) {
22         int sectionValue = maxValue / (numReduceTasks - 1);
23         int count = 0;
24         while ((key.hashCode() - sectionValue * count) > sectionValue) {
25             count++;
26         }
27         keySection = numReduceTasks - 1 - count;
28     }
29     return keySection;
30 }
31  
32 }

Comparator:

01 /**
02  * int的key按照降序排列
03  *
04  * @author zhangdonghao
05  *
06  */
07 public class IntKeyAscComparator extends WritableComparator {
08  
09 protected IntKeyAscComparator() {
10     super(IntWritable.classtrue);
11  
12 }
13  
14 @Override
15 public int compare(WritableComparable a, WritableComparable b) {
16     return -super.compare(a, b);
17 }
18  
19 }

job的关键设置:

01 /**
02      * 这里是map输出的key和value类型
03      */
04     job.setOutputKeyClass(IntWritable.class);
05     job.setOutputValueClass(Text.class);
06  
07     job.setMapperClass(SortIntValueMapper.class);
08     // job.setCombinerClass(WordCountReduce.class);
09     job.setReducerClass(SortIntValueReduce.class);
10     // key按照降序排列
11     job.setSortComparatorClass(IntKeyAscComparator.class);
12  
13     job.setPartitionerClass(KeySectionPartitioner.class);
14  
15     job.setInputFormatClass(TextInputFormat.class);
16     job.setOutputFormatClass(TextOutputFormat.class);
17             /**
18               *这里可以放输入目录数组,也就是可以把上一个job所有的结果都放进去
19              **/
20             FileInputFormat.setInputPaths(job, inputPath);
21  
22     FileOutputFormat.setOutputPath(job,outputPath);

大概就是这样,亲测可用。(^__^)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值