Hadoop学习笔记(8)

——实战 做个倒排索引

倒排索引是文档检索系统中最常用数据结构。根据单词反过来查在文档中出现的频率,而不是根据文档来,所以称倒排索引(Inverted Index)。结构如下:

这张索引表中, 每个单词都对应着一系列的出现该单词的文档,权表示该单词在该文档中出现的次数。现在我们假定输入的是以下的文件清单:

T1 : hello world hello china

T2 : hello hadoop

T3 : bye world bye hadoop bye bye

输入这些文件,我们最终将会得到这样的索引文件:

bye    T3:4;

china    T1:1;

hadoop    T2:1;T3:1;

hello    T1:2;T2:1;

world    T1:1;T3:1;

接下来,我们就是要想办法利用hadoop来把这个输入,变成输出。从上一章中,其实也就是分析如何将hadoop中的步骤个性化,让其工作。整个步骤中,最主要的还是map和reduce过程,其它的都可称之为配角,所以我们先来分析下map和reduce的过程将会是怎样?

首先是Map的过程。Map的输入是文本输入,一条条的行记录进入。输出呢?应该包含:单词、所在文件、单词数。 Map的输入是key-value。 那这三个信息谁是key,谁是value呢? 数量是需要累计的,单词数肯定在value里,单词在key中,文件呢?不同文件内的相同单词也不能累加的,所以这个文件应该在key中。这样key中就应该包含两个值:单词和文件,value则是默认的数量1,用于后面reduce来进行合并。

所以Map后的结果应该是这样的:

Key value

Hello;T1 1

Hello:T1 1

World:T1 1

China:T1 1

Hello:T2 1

即然这个key是复合的,所以常归的类型已经不能满足我们的要求了,所以得设置一个复合健。复合健的写法在上一章中描述到了。所以这里我们就直接上代码:

  1. public static class MyType implements WritableComparable<MyType>{
  2.       public MyType(){
  3.       }
  4.  
  5.       private String word;
  6.       public String Getword(){return word;}
  7.       public void Setword(String value){ word = value;}
  8.  
  9.       private String filePath;
  10.       public String GetfilePath(){return filePath;}
  11.       public void SetfilePath(String value){ filePath = value;}
  12.  
  13.       @Override
  14.       public void write(DataOutput out) throws IOException {
  15.          out.writeUTF(word);
  16.          out.writeUTF(filePath);
  17.       }
  18.  
  19.       @Override
  20.       public void readFields(DataInput in) throws IOException {
  21.          word = in.readUTF();
  22.          filePath = in.readUTF();
  23.       }
  24.  
  25.       @Override
  26.       public int compareTo(MyType arg0) {
  27.             if (word != arg0.word)
  28.                return word.compareTo(arg0.word);
  29.          return filePath.compareTo(arg0.filePath);
  30.       }
  31. }

有了这个复合健的定义后,这个Map函数就好写了:

  1. public static class InvertedIndexMapper extends
  2.          Mapper<Object, Text, MyType, Text> {
  3.  
  4.       public void map(Object key, Text value, Context context)
  5.             throws InterruptedException, IOException {
  6.  
  7.          FileSplit split = (FileSplit) context.getInputSplit();
  8.          StringTokenizer itr = new StringTokenizer(value.toString());
  9.  
  10.          while (itr.hasMoreTokens()) {
  11.             MyType keyInfo = new MyType();
  12.             keyInfo.Setword(itr.nextToken());
  13.             keyInfo.SetfilePath(split.getPath().toUri().getPath().replace("/user/zjf/in/", ""));
  14.             context.write(keyInfo, "));
  15.          }
  16.       }
  17.    }

注意:第13行,路径是全路径的,为了看起来方便,我们把目录替换掉,直接取文件名。

有了Map,接下来就可以考虑Recude了,以及在Map之后的Combine。Map的输出的Key类型是MyType,所以Reduce以及Combine的输入就必须是MyType了。

如果直接将Map的结果送到Reduce后,发现还需要做大量的工作来将Key中的单词再重排一下。所以我们考虑在Reduce前加一个Combine,先将数量进行一轮合并。

这个Combine将会输入下面的值:

Key value

bye    T3:4;

china    T1:1;

hadoop    T2:1;

hadoop    T3:1;

hello    T1:2;

hello    T2:1;

world    T1:1;

world    T3:1;

代码如下:

  1. public static class InvertedIndexCombiner extends
  2.          Reducer<MyType, Text, MyType, Text> {
  3.  
  4.       public void reduce(MyType key, Iterable<Text> values, Context context)
  5.             throws InterruptedException, IOException {
  6.          int sum = 0;
  7.          for (Text value : values) {
  8.             sum += Integer.parseInt(value.toString());
  9.          }
  10.          context.write(key, new Text(key.GetfilePath()+ ":" + sum));
  11.       }
  12.    }

有了上面Combine后的结果,再进行Reduce就容易了,只需要将value结果进行合并处理:

  1. public static class InvertedIndexReducer extends
  2.          Reducer<MyType, Text, Text, Text> {
  3.  
  4.       public void reduce(MyType key, Iterable<Text> values, Context context)
  5.             throws InterruptedException, IOException {
  6.          Text result = new Text();
  7.  
  8.          String fileList = new String();
  9.          for (Text value : values) {
  10.             fileList += value.toString() + ";";
  11.          }
  12.          result.set(fileList);
  13.  
  14.          context.write(new Text(key.Getword()), result);
  15.       }
  16.    }

    经过这个Reduce处理,就得到了下面的结果:

bye    T3:4;

china    T1:1;

hadoop    T2:1;T3:1;

hello    T1:2;T2:1;

world    T1:1;T3:1;

最后,MapReduce函数都写完后,就可以挂在Job中运行了。

  1. public static void main(String[] args) throws IOException,
  2.          InterruptedException, ClassNotFoundException {
  3.       Configuration conf = new Configuration();
  4.       System.out.println("url:" + conf.get("fs.default.name"));
  5.  
  6.       Job job = new Job(conf, "InvertedIndex");
  7.       job.setJarByClass(InvertedIndex.class);
  8.       job.setMapperClass(InvertedIndexMapper.class);
  9.       job.setMapOutputKeyClass(MyType.class);
  10.       job.setMapOutputValueClass(Text.class);
  11.  
  12.       job.setCombinerClass(InvertedIndexCombiner.class);
  13.       job.setReducerClass(InvertedIndexReducer.class);
  14.  
  15.       job.setOutputKeyClass(Text.class);
  16.       job.setOutputValueClass(Text.class);
  17.  
  18.       Path path = new Path("out");
  19.       FileSystem hdfs = FileSystem.get(conf);
  20.       if (hdfs.exists(path))
  21.          hdfs.delete(path, true);
  22.  
  23.       FileInputFormat.addInputPath(job, new Path("in"));
  24.       FileOutputFormat.setOutputPath(job, new Path("out"));
  25.       job.waitForCompletion(true);
  26. }

注:这里为了调试方便,我们把in和out都写死,不用传入执行参数了,并且,每次执行前,判断out文件夹是否存在,如果存在则删除。

Hadoop学习笔记(8) ——实战 做个倒排索引的更多相关文章

  1. 十六、Hadoop学习笔记————Zookeeper实战

    所有服务器都会先将自己的服务器信息注册到servers中,然后每台服务器都会尝试注册master,哪台注册成功,则哪台就是master服务器. 所有的服务器都会关注master节点的删除事件,这样通过 ...

  2. Hadoop学习笔记系列

    Hadoop学习笔记系列   一.为何要学习Hadoop? 这是一个信息爆炸的时代.经过数十年的积累,很多企业都聚集了大量的数据.这些数据也是企业的核心财富之一,怎样从累积的数据里寻找价值,变废为宝炼 ...

  3. Hadoop学习笔记—22.Hadoop2.x环境搭建与配置

    自从2015年花了2个多月时间把Hadoop1.x的学习教程学习了一遍,对Hadoop这个神奇的小象有了一个初步的了解,还对每次学习的内容进行了总结,也形成了我的一个博文系列<Hadoop学习笔 ...

  4. Hadoop学习笔记(6) ——重新认识Hadoop

    Hadoop学习笔记(6) ——重新认识Hadoop 之前,我们把hadoop从下载包部署到编写了helloworld,看到了结果.现是得开始稍微更深入地了解hadoop了. Hadoop包含了两大功 ...

  5. Hadoop学习笔记(5) ——编写HelloWorld(2)

    Hadoop学习笔记(5) ——编写HelloWorld(2) 前面我们写了一个Hadoop程序,并让它跑起来了.但想想不对啊,Hadoop不是有两块功能么,DFS和MapReduce.没错,上一节我 ...

  6. Hadoop学习笔记(1) ——菜鸟入门

    Hadoop学习笔记(1) ——菜鸟入门 Hadoop是什么?先问一下百度吧: [百度百科]一个分布式系统基础架构,由Apache基金会所开发.用户可以在不了解分布式底层细节的情况下,开发分布式程序. ...

  7. Hadoop学习笔记(两)设置单节点集群

    本文描写叙述怎样设置一个单一节点的 Hadoop 安装.以便您能够高速运行简单的操作,使用 Hadoop MapReduce 和 Hadoop 分布式文件系统 (HDFS). 參考官方文档:Hadoo ...

  8. Hadoop学习笔记(1)(转)

    Hadoop学习笔记(1) ——菜鸟入门 Hadoop是什么?先问一下百度吧: [百度百科]一个分布式系统基础架构,由Apache基金会所开发.用户可以在不了解分布式底层细节的情况下,开发分布式程序. ...

  9. Hadoop学习笔记(9) ——源码初窥

    Hadoop学习笔记(9) ——源码初窥 之前我们把Hadoop算是入了门,下载的源码,写了HelloWorld,简要分析了其编程要点,然后也编了个较复杂的示例.接下来其实就有两条路可走了,一条是继续 ...

随机推荐

  1. django系列6--Ajax06 使用插件,Sweet-Alert插件

    使用SweetAlert插件 GitHub上的下载链接 下载完成后放入django项目静态目录下,在html文件中引入静态文件,下面是script部分 $(".btn-danger" ...

  2. 微信小程序——扫码后提示“打开失败缺少ID”

    解决步骤: 进入通讯录tab->点击右上角添加朋友->搜索框输入:recover,拉到最底下选择小程序进行修复操作 参考:https://developers.weixin.qq.com/ ...

  3. “全栈2019”Java第一百零九章:匿名内部类实现唯一抽象类或接口

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  4. “全栈2019”Java第一百零八章:匿名内部类与final关键字

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  5. objectARX 关于MFC类向导 无法向此非CCmdTarget派生类添加任何命令 的解决方式

    objectARX 关于MFC类向导 无法向此非CCmdTarget派生类添加任何命令  的解决方式 图文By edata ,转载注明出处 http://www.cnblogs.com/edata 1 ...

  6. 《快学Scala》第五章 类

    关于case class和普通class的区别,可以参考: https://www.iteblog.com/archives/1508.html

  7. 由一个场景分析Mysql的join原理

    背景 这几天同事写报表,sql语句如下 select * from `sail_marketing`.`mk_coupon_log` a left join `cp0`.`coupon` c on c ...

  8. [转] YUM 源优先级插件:Yum Priorities

    Linux 发行版比较多,同时还有很多个人或组织维护了某些特定用途的安装/升级源.Yum Priorities 插件可以用来强制保护源.它通过给各个源设定不同的优先级,使得系统管理员可以将某些源(比如 ...

  9. 利用python 学习数据分析 (学习四)

    内容学习自: Python for Data Analysis, 2nd Edition         就是这本 纯英文学的很累,对不对取决于百度翻译了 前情提要: 各种方法贴: https://w ...

  10. 集合之六:Map接口

    Map接口概述 Map接口中的集合和Collection接口中的集合在存储数据的格式上有很大的不同,Map接口下的内容是以<K , V> ,键值对的形式存储的,我们查询API,Map接口的 ...