MapReduce-TopK

友情提示:更多有关大数据、人工智能方面技术文章请关注博主个人微信公众号:高级大数据架构师

上一篇博客中简单的写了一个MapReduce的程序,其中只重写了map()和reduce()方法,但里面还有cleanup(Context context),
setup(Context context)和run(Context context)方法可以可以重写
这一个实例,我们就说明一下cleanup(Context context),setup(Context context)这两个方法的作用以及在本例中的用法,
以及TopK的MapReduce的写法,对于TopK这里将根据实际情况用种方法来实现,一种是只关注TopK的数值,另一种情况是不但

 

关心TopK的数值,而且还要输出TopK的相关信息

 

首先讲解setup(Context context)和cleanup(Context context)的作用
以下是hadoop1.2.1中截取换源码,位于org.apache.hadoop.mapreduce.Mapper类中

 

[java] view plain copy

  1. /** 
  2.    * Called once at the beginning of the task. 
  3.    */  
  4.   protected void setup(Context context ) throws IOException, InterruptedException {  
  5.     // NOTHING  
  6.   }  
  7.   /** 
  8.    * Called once at the end of the task. 
  9.    */  
  10.   protected void cleanup(Context context ) throws IOException, InterruptedException {  
  11.     // NOTHING  
  12.   }  

由注释可知道setup()方法是在任务开始之前调用一次,cleanup()方法则是在任务结束之后调用一次
所以这两个方法可以用来对初始的变量进行赋值,或文件的分发,以及任务执行完成过后对资源的释放
在本例中我们会用这两个方法来做TopK中K值的指定。在以后的博客中我们还会有其它的用法。


以下的代码是Mapper类中的一个重要的方法,从run()可以看到任务提交后首先执行setup(context)方法
然后通过判断是否还有下一个值,不停的调用map()方法,最后在调用cleanup(context)方法,这就是一Map执行的顺序
这里不详细讲解nextKeyValue()方法,后面在讲解RecordReader的定制的博客中会做详细的讲解,参见MapReduce-xml文件的处理-定制InputFormat及定制RecordReader

 

 

[java] view plain copy

  1. /** 
  2.   * Expert users can override this method for more complete control over the 
  3.   * execution of the Mapper. 
  4.   * @param context 
  5.   * @throws IOException 
  6.   */  
  7.  public void run(Context context) throws IOException, InterruptedException {  
  8.    setup(context);  
  9.    try {  
  10.     //map通过这里反复调用RecordReader的方法  
  11.     while (context.nextKeyValue()) {  
  12.     //context.getCurrentKey()在MapContext的方法中调用相关RecordReader的方法  
  13.         /** 
  14.          * @Override 
  15.          *      public KEYIN getCurrentKey() throws IOException, InterruptedException { 
  16.          *          return reader.getCurrentKey(); 
  17.          *      } 
  18.          */  
  19.        map(context.getCurrentKey(), context.getCurrentValue(), context);  
  20.      }  
  21.    } finally {  
  22.      cleanup(context);  
  23.    }  
  24.  }  

接下来我们开始写TopK的第一个情况的程序
测试数据:
uid,name,cost
1,mr1,3234
2,mr2,123
3,mr3,9877
4,mr4,348
5,mr5,12345
6,mr6,6646
7,mr7,98
8,mr8,12345
现在要提取出消费最高的金额前3的是多少
map阶段:

 

[java] view plain copy

  1. import java.io.IOException;  
  2. import java.util.Arrays;  
  3. import org.apache.hadoop.io.IntWritable;  
  4. import org.apache.hadoop.io.LongWritable;  
  5. import org.apache.hadoop.io.Text;  
  6. import org.apache.hadoop.mapreduce.Mapper;  
  7.   
  8. public class TopKMapper extends Mapper<LongWritable, Text, IntWritable, IntWritable> {  
  9.       
  10.     int len;  
  11.     int[] tmp;  
  12.   
  13.     /** 
  14.      * Map任务启动的时候调用 
  15.      */  
  16.     @Override  
  17.     protected void setup( Context context)  
  18.             throws IOException, InterruptedException {  
  19.         /** 
  20.          * 通过context获取任务启动时传入的TopK的K值 
  21.          */  
  22.         len = context.getConfiguration().getInt("K"10);  
  23.         /** 
  24.          * 创建一个数组来存放topK的值 
  25.          */  
  26.         tmp = new int[len + 1];  
  27.     }  
  28.   
  29.     @Override  
  30.     protected void map(LongWritable key, Text value, Context context)  
  31.             throws IOException, InterruptedException {  
  32.         String line = value.toString();  
  33.         String[] arr = line.split(",");  
  34.         if(arr.length == 3) {  
  35.             addParm(Integer.valueOf(arr[2]));  
  36.         }  
  37.     }  
  38.     /** 
  39.      * 把新取出的数值放到数组中的第一位,并按从小到大排序 
  40.      * 这样下一次放入时就把最小的数值去除掉了 
  41.      */  
  42.     private void addParm(int parm) {  
  43.         tmp[0] = parm;  
  44.         Arrays.sort(tmp);  
  45.     }  
  46.       
  47.     @Override  
  48.     protected void cleanup(Context context)  
  49.             throws IOException, InterruptedException {  
  50.         /** 
  51.          * 把数组中的数值倒序的输出,这样每个Map就输出了自己的TOPK去reduce端 
  52.          */  
  53.         forint i = 1 ; i <= len ; i ++) {  
  54.             context.write(new IntWritable(tmp[i]), new IntWritable(tmp[i]));  
  55.         }  
  56.     }  
  57. }  

reduce阶段:

 

 

[java] view plain copy

  1. import java.io.IOException;  
  2. import java.util.Arrays;  
  3. import org.apache.hadoop.io.IntWritable;  
  4. import org.apache.hadoop.mapreduce.Reducer;  
  5.   
  6. public class TopKReducer extends Reducer<IntWritable, IntWritable, IntWritable, IntWritable> {  
  7.     int len;  
  8.     int[] tmp;  
  9.       
  10.     @Override  
  11.     protected void setup( Context context)  
  12.             throws IOException, InterruptedException {  
  13.         len = context.getConfiguration().getInt("K"10);  
  14.         tmp = new int[len + 1];  
  15.     }  
  16.   
  17.     @Override  
  18.     protected void reduce(IntWritable key, Iterable<IntWritable> values, Context arg2)  
  19.             throws IOException, InterruptedException {  
  20.         for(IntWritable value: values) {  
  21.             addParm(value.get());  
  22.         }  
  23.     }  
  24.   
  25.     private void addParm(int parm) {  
  26.         tmp[0] = parm;  
  27.         Arrays.sort(tmp);  
  28.     }  
  29.       
  30.     @Override  
  31.     protected void cleanup( Context context)  
  32.             throws IOException, InterruptedException {  
  33.         forint i = len; i > 0 ; i --) {  
  34.             context.write(new IntWritable(len - i + 1), new IntWritable(tmp[i]));  
  35.         }  
  36.     }  
  37. }  

启动函数:

 

 

[java] view plain copy

  1. import org.apache.hadoop.conf.Configuration;  
  2. import org.apache.hadoop.fs.FileSystem;  
  3. import org.apache.hadoop.fs.Path;  
  4. import org.apache.hadoop.io.IntWritable;  
  5. import org.apache.hadoop.mapreduce.Job;  
  6. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;  
  7. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;  
  8.   
  9. public class JobMain {  
  10.     public static void main(String[] args) throws Exception {  
  11.         Configuration configuration = new Configuration();  
  12.         /** 
  13.          * 把传入参数放入Configuration中,map或reduce中可以通过 
  14.          * 获取Configuration来获取传入的参数,这是hadoop传入参数的 
  15.          * 方式之一 
  16.          */  
  17.         configuration.set("K", args[2]);  
  18.         Job job = new Job(configuration, "topK-job");  
  19.         job.setJarByClass(JobMain.class);  
  20.         job.setMapperClass(TopKMapper.class);  
  21.         job.setMapOutputKeyClass(IntWritable.class);  
  22.         job.setMapOutputValueClass(IntWritable.class);  
  23.         job.setReducerClass(TopKReducer.class);  
  24.         job.setOutputKeyClass(IntWritable.class);  
  25.         job.setOutputValueClass(IntWritable.class);  
  26.         FileInputFormat.addInputPath(job, new Path(args[0]));  
  27.         Path outputDir = new Path(args[1]);  
  28.         FileSystem fs = FileSystem.get(configuration);  
  29.         if(fs.exists(outputDir)) {  
  30.             fs.delete(outputDir, true);  
  31.         }  
  32.         FileOutputFormat.setOutputPath(job, outputDir);  
  33.         System.exit(job.waitForCompletion(true)? 01);  
  34.     }  
  35. }  

 

 

测试结果:

 

 

 

测试数据:
uid,name,cost
1,mr1,3234
2,mr2,123
3,mr3,9877
4,mr4,348
5,mr5,12345
6,mr6,6646
7,mr7,98
8,mr8,12345
现在要提取出消费最高的金额前3的name加金额

map阶段:

 

[java] view plain copy

  1. import java.io.IOException;  
  2. import java.util.Map.Entry;  
  3. import java.util.Comparator;  
  4. import java.util.Set;  
  5. import java.util.TreeMap;  
  6. import org.apache.hadoop.io.IntWritable;  
  7. import org.apache.hadoop.io.LongWritable;  
  8. import org.apache.hadoop.io.Text;  
  9. import org.apache.hadoop.mapreduce.Mapper;  
  10.   
  11. public class TopKTreeMapper extends  
  12.         Mapper<LongWritable, Text, IntWritable, Text> {  
  13.   
  14.     private int len;  
  15.     private TreeMap<Integer, String> tmp;  
  16.     private IntWritable key = new IntWritable();  
  17.     private Text value = new Text();  
  18.     /** 
  19.      * Map任务启动的时候调用 
  20.      */  
  21.     @Override  
  22.     protected void setup(Context context) throws IOException,  
  23.             InterruptedException {  
  24.         /** 
  25.          * 通过context获取任务启动时传入的TopK的K值 
  26.          */  
  27.         len = context.getConfiguration().getInt("K"10);  
  28.         tmp = new TreeMap<Integer, String>(new Comparator<Integer>() {  
  29.   
  30.             @Override  
  31.             public int compare(Integer o1, Integer o2) {  
  32.                 return o2-o1;  
  33.             }  
  34.         });  
  35.     }  
  36.   
  37.     @Override  
  38.     protected void map(LongWritable key, Text value, Context context)  
  39.             throws IOException, InterruptedException {  
  40.         String line = value.toString();  
  41.         String[] arr = line.split(",");  
  42.         if (arr.length == 3) {  
  43.             addParm(Integer.valueOf(arr[2]), arr[1]);  
  44.         }  
  45.     }  
  46.   
  47.     /** 
  48.      * 把新取出的数值放到数组中的第一位,并按从小到大排序 这样下一次放入时就把最小的数值去除掉了 
  49.      */  
  50.     private void addParm(int parm, String name) {  
  51.         tmp.put(parm, name);  
  52.         if (tmp.size() > len) {  
  53.             tmp.remove(tmp.lastKey());  
  54.         }  
  55.     }  
  56.   
  57.     @Override  
  58.     protected void cleanup(Context context) throws IOException,  
  59.             InterruptedException {  
  60.         Set<Entry<Integer, String>> set = tmp.entrySet();  
  61.         for (Entry<Integer, String> entry : set) {  
  62.             key.set(entry.getKey());  
  63.             value.set(entry.getValue());  
  64.             context.write(key, value);  
  65.         }  
  66.     }  
  67. }  

reduce阶段:

 

 

[java] view plain copy

  1. import java.io.IOException;  
  2. import java.util.Map.Entry;  
  3. import java.util.Comparator;  
  4. import java.util.Set;  
  5. import java.util.TreeMap;  
  6. import org.apache.hadoop.io.IntWritable;  
  7. import org.apache.hadoop.io.Text;  
  8. import org.apache.hadoop.mapreduce.Reducer;  
  9.   
  10. public class TopKTreeReducer extends Reducer<IntWritable, Text, Text, IntWritable> {  
  11.     int len;  
  12.     private TreeMap<Integer, String> tmp;  
  13.     private IntWritable key = new IntWritable();  
  14.     private Text value = new Text();  
  15.       
  16.     @Override  
  17.     protected void setup( Context context)  
  18.             throws IOException, InterruptedException {  
  19.         len = context.getConfiguration().getInt("K"10);  
  20.         tmp = new TreeMap<Integer, String>(new Comparator<Integer>() {  
  21.             @Override  
  22.             public int compare(Integer o1, Integer o2) {  
  23.                 return o2-o1;  
  24.             }  
  25.         });  
  26.     }  
  27.   
  28.     @Override  
  29.     protected void reduce(IntWritable key, Iterable<Text> values, Context arg2)  
  30.             throws IOException, InterruptedException {  
  31.         for(Text value : values) {  
  32.             addParm(key.get(), value.toString());  
  33.         }  
  34.     }  
  35.   
  36.     private void addParm(int parm, String name) {  
  37.         tmp.put(parm, name);  
  38.         if (tmp.size() > len) {  
  39.             tmp.remove(tmp.lastKey());  
  40.         }  
  41.     }  
  42.       
  43.     @Override  
  44.     protected void cleanup( Context context)  
  45.             throws IOException, InterruptedException {  
  46.         Set<Entry<Integer, String>> set = tmp.entrySet();  
  47.         for (Entry<Integer, String> entry : set) {  
  48.             key.set(entry.getKey());  
  49.             value.set(entry.getValue());  
  50.             context.write(value, key);  
  51.         }  
  52.     }  
  53. }  

启动函数:

 

 

[java] view plain copy

  1. import org.apache.hadoop.conf.Configuration;  
  2. import org.apache.hadoop.fs.FileSystem;  
  3. import org.apache.hadoop.fs.Path;  
  4. import org.apache.hadoop.io.IntWritable;  
  5. import org.apache.hadoop.io.Text;  
  6. import org.apache.hadoop.mapreduce.Job;  
  7. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;  
  8. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;  
  9.   
  10. public class JobMainTree {  
  11.     public static void main(String[] args) throws Exception {  
  12.         Configuration configuration = new Configuration();  
  13.         /** 
  14.          * 把传入参数放入Configuration中,map或reduce中可以通过 
  15.          * 获取Configuration来获取传入的参数,这是hadoop传入参数的 
  16.          * 方式之一 
  17.          */  
  18.         configuration.set("K", args[2]);  
  19.         Job job = new Job(configuration, "topK-job-tree");  
  20.         job.setJarByClass(JobMainTree.class);  
  21.         job.setMapperClass(TopKTreeMapper.class);  
  22.         job.setMapOutputKeyClass(IntWritable.class);  
  23.         job.setMapOutputValueClass(Text.class);  
  24.         job.setReducerClass(TopKTreeReducer.class);  
  25.         job.setOutputKeyClass(Text.class);  
  26.         job.setOutputValueClass(IntWritable.class);  
  27.         FileInputFormat.addInputPath(job, new Path(args[0]));  
  28.         Path outputDir = new Path(args[1]);  
  29.         FileSystem fs = FileSystem.get(configuration);  
  30.         if(fs.exists(outputDir)) {  
  31.             fs.delete(outputDir, true);  
  32.         }  
  33.         FileOutputFormat.setOutputPath(job, outputDir);  
  34.         System.exit(job.waitForCompletion(true)? 01);  
  35.     }  
  36. }  

运行结果:

 



结论:如果仔细观察结果会发现,第二次运行结果没有重复的最大值出现,重复的值只出现了一次而且是最新进入TreeMap的值,这是因为TreeMap的属性的问题,这里我们不做过多的讨论,这篇博客只是熟悉Mapper类中可重写方法的用途。下一篇博客讲解自定义输入类型,并解决这一篇遗留的即要取name字段又要取cost字段,但还要保留做重复金额的问题。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值