三 概要模式 2) MR倒排索引、性能分析、搜索干扰词。

二  倒排索引

倒排索引(英语:Inverted index),也常被称为反向索引置入档案反向档案,是一种索引方法,被用来存储全文搜索下某个单词在一个文档或者一组文档中的存储位置映射。它是文档检索系统中最常用的数据结构

有两种不同的反向索引形式:

  • 一条记录的水平反向索引(或者反向档案索引)包含每个引用单词的文档的列表
  • 一个单词的水平反向索引(或者完全反向索引)又包含每个单词在一个文档中的位置。[1]

后者的形式提供了更多的兼容性(比如短语搜索),但是需要更多的时间和空间来创建。

使用场景:

倒排索引通常用在需要快速搜索查询响应的场景。可以对一个查询的结果进行预处理并存储在一个数据库中。

英文为例,下面是要被索引的文本:

  • "it is what it is"
  • "what is it"
  • "it is a banana"

我们就能得到下面的反向文件索引:

 "a":      {2}
 "banana": {2}
 "is":     {0, 1, 2}
 "it":     {0, 1, 2}
 "what":   {0, 1}

检索的条件"what""is" 和 "it" 将对应这个集合

对相同的文字,我们得到后面这些完全反向索引,有文档数量和当前查询的单词结果组成的的成对数据。 同样,文档数量和当前查询的单词结果都从零开始。所以,"banana": {(2, 3)} 就是说 "banana"在第三个文档里 (),而且在第三个文档的位置是第四个单词(地址为 3)。

"a":      {(2, 2)}
"banana": {(2, 3)}
"is":     {(0, 1), (0, 4), (1, 1), (2, 1)}
"it":     {(0, 0), (0, 3), (1, 2), (2, 0)}
"what":   {(0, 2), (1, 0)}

如果我们执行短语搜索"what is it" 我们得到这个短语的全部单词各自的结果所在文档为文档0和文档1。但是这个短语检索的连续的条件仅仅在文档1得到。

来源: <https://zh.wikipedia.org/wiki/%E5%80%92%E6%8E%92%E7%B4%A2%E5%BC%95>

2.分析和设计

(1)Map过程

首先使用默认的TextInputFormat类对输入文件进行处理,得到文本中每行的偏移量及其内容,Map过程首先必须分析输入的<key, value>对,得到倒排索引中需要的三个信息:单词、文档URI和词频,如图所示:

存在两个问题,第一:<key, value>对只能有两个值,在不使用Hadoop自定义数据类型的情况下,需要根据情况将其中的两个值合并成一个值,作为value或key值;

第二,通过一个Reduce过程无法同时完成词频统计和生成文档列表,所以必须增加一个Combine过程完成词频统计


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

public static class InvertedIndexMapper extends Mapper<Object, Text, Text, Text> {

    private Text keyInfo = new Text();  //存储单词和URI的组合

    private Text valueInfo = new Text();//存储词频

    private FileSplit split;            //存储Split对象

    

    public void map(Object key, Text value, Context context) throws IOException, InterruptedException {

        //获得<key,value>对所属的FileSplit对象

        split = (FileSplit)context.getInputSplit();

        StringTokenizer itr = new StringTokenizer(value.toString());

        

        while(itr.hasMoreTokens()) {

            //key值由单词和URI组成,如"MapReduce:1.txt"

            keyInfo.set(itr.nextToken() + ":" + split.getPath().toString());

            // 词频初始为1

            valueInfo.set("1");

            context.write(keyInfo, valueInfo);

        }

    }

}

(2)Combine过程

将key值相同的value值累加,得到一个单词在文档中的词频,如图


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

public static class InvertedIndexCombiner extends Reducer<Text, Text, Text, Text> {

    private Text info = new Text();

    public void reduce(Text key, Iterable<Text>values, Context context) throws IOException, InterruptedException {

        //统计词频

        int sum = 0;

        for(Text value : values) {

            sum += Integer.parseInt(value.toString());

        }

        int splitIndex= key.toString().indexOf(":");

        

        //重新设置value值由URI和词频组成

        info.set(key.toString().substring(splitIndex + 1) + ":" + sum);

        //重新设置key值为单词

        key.set(key.toString().substring(0, splitIndex));

        context.write(key, info);

    }

}

(3)Reduce过程

讲过上述两个过程后,Reduce过程只需将相同key值的value值组合成倒排索引文件所需的格式即可,剩下的事情就可以直接交给MapReduce框架进行处理了


1

2

3

4

5

6

7

8

9

10

11

12

public static class InvertedIndexReducer extends Reducer<Text, Text, Text, Text> {

    private Text result = new Text();

    public void reducer(Text key, Iterable<Text>values, Context context) throws IOException, InterruptedException {

        //生成文档列表

        String fileList = new String();

        for(Text value : values) {

            fileList += value.toString() + ";";

        }

        result.set(fileList);

        context.write(key, result);

    }

}

完整代码如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

import java.io.IOException;

import java.util.StringTokenizer;

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.Path;

import org.apache.hadoop.io.IntWritable;

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.input.FileSplit;

import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import org.apache.hadoop.util.GenericOptionsParser;

public class InvertedIndex {

    public static class InvertedIndexMapper extends Mapper<Object, Text, Text, Text> {

        private Text keyInfo = new Text();

        private Text valueInfo = new Text();

        private FileSplit split;

        

        public void map(Object key, Text value, Context context) throws IOException, InterruptedException {

            split = (FileSplit)context.getInputSplit();

            StringTokenizer itr = new StringTokenizer(value.toString());

            

            while(itr.hasMoreTokens()) {

                keyInfo.set(itr.nextToken() + ":" + split.getPath().toString());

                valueInfo.set("1");

                context.write(keyInfo, valueInfo);

            }

        }

        

    }

    public static class InvertedIndexCombiner extends Reducer<Text, Text, Text, Text> {

        private Text info = new Text();

        public void reduce(Text key, Iterable<Text>values, Context context) throws IOException, InterruptedException {

            int sum = 0;

            for(Text value : values) {

                sum += Integer.parseInt(value.toString());

            }

            int splitIndex= key.toString().indexOf(":");

            info.set(key.toString().substring(splitIndex + 1) + ":" + sum);

            key.set(key.toString().substring(0, splitIndex));

            context.write(key, info);

        }

    }

    public static class InvertedIndexReducer extends Reducer<Text, Text, Text, Text> {

        private Text result = new Text();

        public void reducer(Text key, Iterable<Text>values, Context context) throws IOException, InterruptedException {

            String fileList = new String();

            for(Text value : values) {

                fileList += value.toString() + ";";

            }

            result.set(fileList);

            context.write(key, result);

        }

    }

    public static void main(String[] args) throws Exception{

        // TODO Auto-generated method stub

        Configuration conf = new Configuration();

        String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();

        if(otherArgs.length != 2) {

            System.err.println("Usage: wordcount <in> <out>");

            System.exit(2);

        }

        Job job = new Job(conf, "InvertedIndex");

        job.setJarByClass(InvertedIndex.class);

        job.setMapperClass(InvertedIndexMapper.class);

        job.setMapOutputKeyClass(Text.class);

        job.setMapOutputValueClass(Text.class);

        job.setCombinerClass(InvertedIndexCombiner.class);

        job.setReducerClass(InvertedIndexReducer.class);

        

        job.setOutputKeyClass(Text.class);

        job.setOutputValueClass(Text.class);

        

        FileInputFormat.addInputPath(job, new Path(otherArgs[0]));

        FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));

        System.exit(job.waitForCompletion(true) ? 0 : 1);

    }

}

建立一个倒排索引的性能分析:

Mapper 端内容解析的计算成本;需要索引的键的基数;每一个键对应的内容标识符的数目。

解决方案: mapper 中对文本或者其他格式的内容解析有时是 MR job 中计算最密集的操作。特别是对类似 XML 或者 JSON 这样的半结构化数据来说更是如此,因为这些数据通常需要将任意数量的信息解析成可用对象。 如果唯一键的数目非常巨大,那么 会有更多的数据发送至 Reduce。 这时候应该通过增加 reduce 的数目来提高 reduce 阶段的并行处理能力。

热点分析:倒排索引计算经常会出现索引存在热点的情况,因为索引键很少均匀分布。如:and,the,are 中文 “一”,“的”等。  由于这些词出现的频率非常高,那么这个reduce 将会异常繁忙, 这将拖累整个 job 的 map 并行进度。 为了规避这个问题。可以选择忽略一些对最终结果没有意义的高词频索引词(索引干扰词)。 如果还要更快,需要定制 partitioner 来均匀处理较高词频有意义词

来自为知笔记(Wiz)

时间: 2024-10-14 04:12:22

三 概要模式 2) MR倒排索引、性能分析、搜索干扰词。的相关文章

三 概要模式 3) MR计数器计数 。无 reduce 计数

计数器模式讲解: 先讲一下,就是说只用 Map 阶段  不需要 Reduce . 也就是说去掉了中间输出,而是Map 直接输出结果.大大提高了 MR 的效率且节省了 MR 中间输出读入的 IO . Hadoop 框架支持 在 MR 结束时,只需要简单的获取框架的计数器信息并将结果写入到任意地方. 包括日志.本地文件系统.HDFS 等.  Hadoop 框架中内置了一些计数器的支持. 例如输入.输出的记录数和字节数. Hadoop 支持开发者根据自己的需要创建自定义计数器. 适用场景: 在一个大数

Trie性能分析之敏感词过滤golang

package util import ( "strings" ) type Node struct { //rune表示一个utf8字符 char rune Data interface{} parent *Node Depth int //childs 用来当前节点的所有孩子节点 childs map[rune]*Node term bool } type Trie struct { root *Node size int } func NewNode() *Node { retu

PHP 性能分析第三篇: 性能调优实战

注意:本文是我们的 PHP 性能分析系列的第三篇,点此阅读 PHP 性能分析第一篇: XHProf & XHGui 介绍 ,或  PHP 性能分析第二篇: 深入研究 XHGui. 在本系列的 第一篇 中,我们介绍了 XHProf .而在 第二篇 中,我们深入研究了 XHGui UI, 现在最后一篇,让我们把 XHProf /XHGui 的知识用到工作中! 性能调优 不用运行的代码才是绝好的代码.其他只是好的代码.所以,性能调优时,最好的选择是首先确保运行尽可能少的代码. OpCode 缓存 首先

mysql性能分析工具

一.EXPALIN 在SQL语句之前加上EXPLAIN关键字就可以获取这条SQL语句执行的计划 那么返回的这些字段是什么呢? 我们先关心一下比较重要的几个字段: 1. select_type 查询类型 1)simple 简单查询,没有UNION和子查询 2)priamry 主查询,有UNION或子查询的最外层查询 3)union 联合查询,有UNION的第二个和以后的查询 4)subquery 子查询,有子查询的除FROM包含的子查询 5)derived 派生查询,FROM包含的子查询 6)de

三种Linux性能分析工具的比较

无论是在CPU设计.服务器研发还是存储系统开发的过程中,性能总是一个绕不过去的硬指标.很多时候,我们发现系统功能完备,但就是性能不尽如意,这时候就需要找到性能瓶颈.进行优化.首先我们需要结合硬件特点.操作系统和应用程序的特点深入了解系统内部的运行机制.数据流图和关键路径,最好找出核心模块.建立起抽象模型:接着需要利用各种性能分析工具,探测相关模块的热点路径.耗时统计和占比.在这方面,Linux操作系统自带了多种灵活又具有专对性的工具,此外一些厂家也开源了不少优秀的性能分析工具.下面就结合笔者最近

Android 常用的性能分析工具详解:GPU呈现模式, TraceView, Systrace, HirearchyViewer(转)

此篇将重点介绍几种常用的Android性能分析工具: 一.Logcat 日志 选取Tag=ActivityManager,可以粗略地知道界面Displaying的时间消耗.当我们打开一个Activity的时候,log会打印一串log如下: I/ActivityManager﹕ Displayed xxx.xxx.xxx/TestActivity: +1s272ms (total +3s843ms) 第一个时间表示系统接受到打开的intent到TestActivity界面显示出来的时间1.272秒

MySQL 索引性能分析概要

上一篇文章 MySQL 索引设计概要 介绍了影响索引设计的几大因素,包括过滤因子.索引片的宽窄与大小以及匹配列和过滤列.在文章的后半部分介绍了 数据库索引设计与优化 一书中,理想的三星索引的设计流程和套路,到目前为止虽然我们掌握了单表索引的设计方法,但是却没有分析预估索引耗时的能力. 在本文中,我们将介绍书中提到的两种分析索引性能的方法:基本问题法(BQ)和快速估算上限法(QUBE),这两种方法能够帮助我们快速分析.估算索引的性能,及时发现问题. 基本问题法 当我们需要考虑对现有的 SELECT

C++应用程序性能优化(三)——C++语言特性性能分析

C++应用程序性能优化(三)--C++语言特性性能分析 一.C++语言特性性能分析简介 通常大多数开发人员认为,汇编语言和C语言比较适合编写对性能要求非常高的程序,C++语言主要适用于编写复杂度非常高但性能要求并不是很高的程序.因为大多数开发人员认为,C++语言设计时因为考虑到支持多种编程模式(如面向对象编程和范型编程)以及异常处理等,从而引入了太多新的语言特性.新的语言特性往往使得C++编译器在编译程序时插入了很多额外的代码,会导致最终生成的二进制代码体积膨胀,而且执行速度下降.但事实并非如此

SQL2005性能分析一些细节功能你是否有用到?(三)

原文:SQL2005性能分析一些细节功能你是否有用到?(三) 继上篇: SQL2005性能分析一些细节功能你是否有用到?(二) 第一: SET STATISTICS PROFILE ON 当我们比较查询计划中那一个最好时,事实上我们更愿意用SET STATISTICS PROFILE ON,而不是SET SHOWPLAN_TEXT ON.它可以告诉你每种选择的或多或少的查询消耗情况;你还可以同时运行两个或更多查询来看哪个执行的最好. 运行SET STATISTICS PROFILE ON 后,发