在上一节中我们求重叠次数最多的前三个的时候我们使用的是变量,当等于三的时候就return,其实这样做有缺点,虽然return了可是后面的reduce还是会照样执行。这样会影响执行效率。接下来我们优化一下。
优化思路:想办法让worker将那一堆数据看成一组,这样的话worker就会将那一堆数据只调用一次reduce
这样就涉及到一个知识点,worker是如何让key相同的为一组的?我们要知道我们的数据本质上是在文件中的。文件中的内容其实是序列化的结果。这样就有两种比较是否想通过的方法:
第一种:直接比较文件中序列化的字节
第二种:读出字节并反序列化变成java对象再比较
那么如何比较两个对象是否相等呢?这里它做的没有这么死,它提供了一种方法,这个方法给你两个对象,然后由你来告诉他这两个对象是否相等。
worker是通过下面的接口实现类中的方法来判断两个key是否相等,以便于是否看成同一组:
WritableComparator类:
compare(key1,key2){
return 0; //这里如果return 0,那么任何两个对象都会相等。
}
所以如果想实现我们的功能,我们可以继承这个父类,然后告诉worker别用他默认的方法了,用我们自己重写的方法。
compare(WritableComparable a, WritableComparable b)我们应该用这个方法,这个方法会帮我们从这个文件反序列化成对象。但是我们用这个方法的话我们得告诉他拿到字节之后帮我们反序列化成什么类型,所以就需要我们在构造方法中声明。
public TopGroupingComparator() {
super(Count.class, true);
}
这里的第二个参数为是否帮我们序列化成对象
以下为分组比较器的代码:
package com.test.linecount;
import org.apache.hadoop.io.WritableComparable;
import org.apache.hadoop.io.WritableComparator;
/**
* 分组比较器:用来给worker对数据进行分组的时候使用
* worker会利用其中compare方法来判断两个key是否相同,是否可以看成一组
* @author 刘峰瑞
*
* 2018年8月17日下午2:39:10
*/
public class TopGroupingComparator extends WritableComparator{
public TopGroupingComparator() {
super(Count.class, true);
}
@Override
public int compare(WritableComparable a, WritableComparable b) {
// TODO Auto-generated method stub
return 0;
}
}
优化后的reduce代码:
package com.test.linecount;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class LineCount3 {
public static class lineMapper extends Mapper<LongWritable, Text, Count, NullWritable>{
Count c = new Count();
@Override
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
String line = value.toString();
String[] split = line.split("\t");
c.setPoint(split[0]);
c.setNo(split[1]);
context.write(c, NullWritable.get());
}
}
public static class lineReducer extends Reducer<Count, NullWritable, Count, NullWritable>{
@Override
protected void reduce(Count key, Iterable<NullWritable> values,Context context) throws IOException, InterruptedException {
int count = 0;
for (NullWritable nullWritable : values) {
count++;
if(count==3) return ;
context.write(key, NullWritable.get());
}
}
}
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
Configuration conf = new Configuration();
Job job = Job.getInstance(conf);
job.setJarByClass(LineCount3.class);
job.setMapperClass(lineMapper.class);
job.setReducerClass(lineReducer.class);
job.setMapOutputKeyClass(Count.class);
job.setMapOutputValueClass(NullWritable.class);
job.setOutputKeyClass(Count.class);
job.setOutputValueClass(NullWritable.class);
//指定reduce端的worker在做数据分组的时候所用的分组比较器
job.setGroupingComparatorClass(TopGroupingComparator.class);
FileInputFormat.setInputPaths(job, new Path(args[0]));
FileSystem fs = FileSystem.get(conf);
Path p = new Path(args[1]);
if(fs.exists(p)){
fs.delete(p,true);
}
FileOutputFormat.setOutputPath(job, p);
job.setNumReduceTasks(1);
boolean res = job.waitForCompletion(true);
System.out.println(res?"mr程序成功执行":"mr程序好像被外星人抓走了");
}
}