本文发表于本人博客

今天接着上次【Hadoop mapreduce自定义排序WritableComparable】文章写,按照顺序那么这次应该是讲解自定义分组如何实现,关于操作顺序在这里不多说了,需要了解的可以看看我在博客园的评论,现在开始。

首先我们查看下Job这个类,发现有setGroupingComparatorClass()这个方法,具体源码如下:

  1. /**
  2. * Define the comparator that controls which keys are grouped together
  3. * for a single call to
  4. * {@link Reducer#reduce(Object, Iterable,
  5. * org.apache.hadoop.mapreduce.Reducer.Context)}
  6. * @param cls the raw comparator to use
  7. * @throws IllegalStateException if the job is submitted
  8. */
  9. public void setGroupingComparatorClass(Class<? extends RawComparator> cls
  10. ) throws IllegalStateException {
  11. ensureState(JobState.DEFINE);
  12. conf.setOutputValueGroupingComparator(cls);
  13. }

从方法的源码可以看出这个方法是定义自定义键分组功能。设置这个自定义分组类必须满足extends RawComparator,那我们可以看下这个类的源码:

  1. /**
  2. * <p>
  3. * A {@link Comparator} that operates directly on byte representations of
  4. * objects.
  5. * </p>
  6. * @param <T>
  7. * @see DeserializerComparator
  8. */
  9. public interface RawComparator<T> extends Comparator<T> {
  10. public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2);
  11. }

然而这个RawComparator是泛型继承Comparator接口的,简单看了下那我们来自定义一个类继承RawComparator,代码如下:

  1. public class MyGrouper implements RawComparator<SortAPI> {
  2. @Override
  3. public int compare(SortAPI o1, SortAPI o2) {
  4. return (int)(o1.first - o2.first);
  5. }
  6. @Override
  7. public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {
  8. int compareBytes = WritableComparator.compareBytes(b1, s1, 8, b2, s2, 8);
  9. return compareBytes;
  10. }
  11.  
  12. }

源码中SortAPI是上节自定义排序中的定义对象,第一个方法从注释可以看出是比较2个参数的大小,返回的是自然整数;第二个方法是在反序列化时比较,所以需要是用字节比较。接下来我们继续看看自定义MyMapper类:

  1. public class MyMapper extends Mapper<LongWritable, Text, SortAPI, LongWritable> {
  2. @Override
  3. protected void map(LongWritable key, Text value,Context context) throws IOException, InterruptedException {
  4. String[] splied = value.toString().split("\t");
  5. try {
  6. long first = Long.parseLong(splied[0]);
  7. long second = Long.parseLong(splied[1]);
  8. context.write(new SortAPI(first,second), new LongWritable(1));
  9. } catch (Exception e) {
  10. System.out.println(e.getMessage());
  11. }
  12. }
  13. }

自定义MyReduce类:

  1. public class MyReduce extends Reducer<SortAPI, LongWritable, LongWritable, LongWritable> {
  2. @Override
  3. protected void reduce(SortAPI key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException {
  4. context.write(new LongWritable(key.first), new LongWritable(key.second));
  5. }
  6.  
  7. }

自定义SortAPI类:

  1. public class SortAPI implements WritableComparable<SortAPI> {
  2. public Long first;
  3. public Long second;
  4. public SortAPI(){
  5.  
  6. }
  7. public SortAPI(long first,long second){
  8. this.first = first;
  9. this.second = second;
  10. }
  11.  
  12. @Override
  13. public int compareTo(SortAPI o) {
  14. return (int) (this.first - o.first);
  15. }
  16.  
  17. @Override
  18. public void write(DataOutput out) throws IOException {
  19. out.writeLong(first);
  20. out.writeLong(second);
  21. }
  22.  
  23. @Override
  24. public void readFields(DataInput in) throws IOException {
  25. this.first = in.readLong();
  26. this.second = in.readLong();
  27.  
  28. }
  29.  
  30. @Override
  31. public int hashCode() {
  32. return this.first.hashCode() + this.second.hashCode();
  33. }
  34.  
  35. @Override
  36. public boolean equals(Object obj) {
  37. if(obj instanceof SortAPI){
  38. SortAPI o = (SortAPI)obj;
  39. return this.first == o.first && this.second == o.second;
  40. }
  41. return false;
  42. }
  43.  
  44. @Override
  45. public String toString() {
  46. return "输出:" + this.first + ";" + this.second;
  47. }
  48.  
  49. }

接下来准备数据,数据如下:

  1. 1 2
  2. 1 1
  3. 3 0
  4. 3 2
  5. 2 2
  6. 1 2

上传至hdfs://hadoop-master:9000/grouper/input/test.txt,main代码如下:

  1. public class Test {
  2. static final String OUTPUT_DIR = "hdfs://hadoop-master:9000/grouper/output/";
  3. static final String INPUT_DIR = "hdfs://hadoop-master:9000/grouper/input/test.txt";
  4. public static void main(String[] args) throws Exception {
  5. Configuration conf = new Configuration();
  6. Job job = new Job(conf, Test.class.getSimpleName());
  7. job.setJarByClass(Test.class);
  8. deleteOutputFile(OUTPUT_DIR);
  9. //1设置输入目录
  10. FileInputFormat.setInputPaths(job, INPUT_DIR);
  11. //2设置输入格式化类
  12. job.setInputFormatClass(TextInputFormat.class);
  13. //3设置自定义Mapper以及键值类型
  14. job.setMapperClass(MyMapper.class);
  15. job.setMapOutputKeyClass(SortAPI.class);
  16. job.setMapOutputValueClass(LongWritable.class);
  17. //4分区
  18. job.setPartitionerClass(HashPartitioner.class);
  19. job.setNumReduceTasks(1);
  20. //5排序分组
  21. job.setGroupingComparatorClass(MyGrouper.class);
  22. //6设置在一定Reduce以及键值类型
  23. job.setReducerClass(MyReduce.class);
  24. job.setOutputKeyClass(LongWritable.class);
  25. job.setOutputValueClass(LongWritable.class);
  26. //7设置输出目录
  27. FileOutputFormat.setOutputPath(job, new Path(OUTPUT_DIR));
  28. //8提交job
  29. job.waitForCompletion(true);
  30. }
  31.  
  32. static void deleteOutputFile(String path) throws Exception{
  33. Configuration conf = new Configuration();
  34. FileSystem fs = FileSystem.get(new URI(INPUT_DIR),conf);
  35. if(fs.exists(new Path(path))){
  36. fs.delete(new Path(path));
  37. }
  38. }
  39. }

执行代码,然后在节点上用终端输入:hadoop fs -text /grouper/output/part-r-00000查看结果:

  1. 1 2
  2. 2 2
  3. 3 0

接下来我们修改下SortAPI类的compareTo()方法:

  1. @Override
  2. public int compareTo(SortAPI o) {
  3. long mis = (this.first - o.first) * -1;
  4. if(mis != 0 ){
  5. return (int)mis;
  6. }
  7. else{
  8. return (int)(this.second - o.second);
  9. }
  10. }

再次执行并查看/grouper/output/part-r-00000文件:

  1. 3 0
  2. 2 2
  3. 1 1

这样我们就得出了同样的数据分组结果会受到排序算法的影响,比如排序是倒序那么分组也是先按照倒序数据源进行分组输出。我们还可以在map函数以及reduce函数中打印记录(过程省略)这样经过对比也得出分组阶段:键值对中key相同(即compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2)方法返回0)的则为一组,当前组再按照顺序选择第一个往缓冲区输出(也许会存储到硬盘)。其它的相同key的键值对就不会再往缓冲区输出了。在百度上检索到这边文章,其中它的分组是把map函数输出的value全部迭代到同一个key中,就相当于上面{key,value}:{1,{2,1,2}},这个结果跟最开始没有自定义分组时是一样的,我们可以在reduce函数输出Iterable<LongWritable> values进行查看,其实我觉得这样的才算是分组吧就像数据查询一样。

在这里我们应该要弄懂分组与分区的区别。分区是对输出结果文件进行分类拆分文件以便更好查看,比如一个输出文件包含所有状态的http请求,那么为了方便查看通过分区把请求状态分成几个结果文件。分组就是把一些相同键的键值对进行计算减少输出;分区之后数据全部还是照样输出到reduce端,而分组的话就有所减少了;当然这2个步骤也是不同的阶段执行。

这次先到这里。坚持记录点点滴滴!

Hadoop mapreduce自定义分组RawComparator的更多相关文章

  1. Hadoop mapreduce自定义分区HashPartitioner

    本文发表于本人博客. 在上一篇文章我写了个简单的WordCount程序,也大致了解了下关于mapreduce运行原来,其中说到还可以自定义分区.排序.分组这些,那今天我就接上一次的代码继续完善实现自定 ...

  2. [Hadoop] - Mapreduce自定义Counter

    在Hadoop的MR程序开发中,经常需要统计一些map/reduce的运行状态信息,这个时候我们可以通过自定义Counter来实现,这个实现的方式是不是通过配置信息完成的,而是通过代码运行时检查完成的 ...

  3. 【Hadoop】Hadoop MR 自定义分组 Partition机制

    1.概念 2.Hadoop默认分组机制--所有的Key分到一个组,一个Reduce任务处理 3.代码示例 FlowBean package com.ares.hadoop.mr.flowgroup; ...

  4. Hadoop mapreduce自定义排序WritableComparable

    本文发表于本人博客. 今天继续写练习题,上次对分区稍微理解了一下,那根据那个步骤分区.排序.分组.规约来的话,今天应该是要写个排序有关的例子了,那好现在就开始! 说到排序我们可以查看下hadoop源码 ...

  5. hadoop的自定义分组实现 (Partition机制)

    hadoop开发中我们会遇到类似这样的问题,比如 如何将不同省份的手机号分别输出到不同的文件中,本片文章将对hadoop内置的Partition类进行重写以解决这个问题. MapReduce的使用者通 ...

  6. Hadoop MapReduce自定义数据类型

    一 自定义数据类型的实现 1.继承接口Writable,实现其方法write()和readFields(), 以便该数据能被序列化后完成网络传输或文件输入/输出: 2.如果该数据需要作为主键key使用 ...

  7. 关于MapReduce中自定义分组类(三)

    Job类  /**    * Define the comparator that controls which keys are grouped together    * for a single ...

  8. 一脸懵逼学习Hadoop中的MapReduce程序中自定义分组的实现

    1:首先搞好实体类对象: write 是把每个对象序列化到输出流,readFields是把输入流字节反序列化,实现WritableComparable,Java值对象的比较:一般需要重写toStrin ...

  9. Hadoop自定义分组Group

    matadata: hadoop a spark a hive a hbase a tachyon a storm a redis a 自定义分组 import org.apache.hadoop.c ...

随机推荐

  1. Android - Style问题

    转自:http://jingyan.baidu.com/article/c910274be7536acd361d2dca.html 转自:http://blog.sina.com.cn/s/blog_ ...

  2. 使用NSTimer实现动画

    1.新建empty AppLication,添加HomeViewController页面, iphone.png图片 2.在 HomeViewController.xib中添加Image View,并 ...

  3. Mybaits中的update

    <update id="update" parameterType="Currency"> UPDATE YZ_SECURITIES_CURRENC ...

  4. java基础---->java中国际化的实现

    应用程序的功能和代码设计考虑在不同地区运行的需要,其代码简化了不同本地版本的生产.开发这样的程序的过程,就称为国际化.今天,我们就开始学习java中国际化的代码实现. Java国际化主要通过如下3个类 ...

  5. 【linux系列】linux防火墙的关闭开启

    即时生效 开启:service iptables start 关闭:service iptables stop 重启后生效 开启:chkconfig iptables on 关闭:chkconfig ...

  6. diamond types are not supported at this language level

    在intellij导入git项目之后出现 diamond types are not supported at this language level错误 或者String等报错 File->P ...

  7. Nginx的安装和配置文件

    一.什么是Nginx 反向代理的高手,可以做web服务器.smtp服务器.ftp服务器,也可以做waf等等.原理,反向代理,收集client请求然后转发给自己lan内的服务器,将请求到的资源回转给客户 ...

  8. 正则表达式—RegEx(RegularExpressio)(三)

    今日随笔,继续写一点关于正则表达式的 知识.前两天介绍了正则表达式验证匹配,提取等一些基本的知识,今天继续分享下它的另一个强大的应用:替换(replace). 开始之前,还是要补一下昨天的内容. 在我 ...

  9. Egret打包App Android热更新(4.1.0)

    官网教程:http://developer.egret.com/cn/github/egret-docs/Native/native/hotUpdate/index.html 详细可看官网教程,我这里 ...

  10. 素数测试算法(基于Miller-Rabin的MC算法) // Fermat素数测试法

    在以往判断一个数n是不是素数时,我们都是采用i从2到sqrt(n)能否整除n.如果能整除,则n是合数;否则是素数.但是该算法的时间复杂度为O(sqrt(n)),当n较大时,时间性能很差,特别是在网络安 ...