二  倒排索引
    倒排索引(英语: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得到。

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 来均匀处理较高词频有意义词



            






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

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

    计数器模式讲解:         先讲一下,就是说只用 Map 阶段  不需要 Reduce . 也就是说去掉了中间输出,而是Map 直接输出结果.大大提高了 MR 的效率且节省了 MR 中间输出读入 ...

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

    package util import ( "strings" ) type Node struct { //rune表示一个utf8字符 char rune Data inter ...

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

    注意:本文是我们的 PHP 性能分析系列的第三篇,点此阅读 PHP 性能分析第一篇: XHProf & XHGui 介绍 ,或  PHP 性能分析第二篇: 深入研究 XHGui. 在本系列的 ...

  4. mysql性能分析工具

    一.EXPALIN 在SQL语句之前加上EXPLAIN关键字就可以获取这条SQL语句执行的计划 那么返回的这些字段是什么呢? 我们先关心一下比较重要的几个字段: 1. select_type 查询类型 ...

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

    此篇将重点介绍几种常用的Android性能分析工具: 一.Logcat 日志 选取Tag=ActivityManager,可以粗略地知道界面Displaying的时间消耗.当我们打开一个Activit ...

  6. MySQL 索引性能分析概要

    上一篇文章 MySQL 索引设计概要 介绍了影响索引设计的几大因素,包括过滤因子.索引片的宽窄与大小以及匹配列和过滤列.在文章的后半部分介绍了 数据库索引设计与优化 一书中,理想的三星索引的设计流程和 ...

  7. for-loop 与 json.Unmarshal 性能分析概要

    原文地址:for-loop 与 json.Unmarshal 性能分析概要 前言 在项目中,常常会遇到循环交换赋值的数据处理场景,尤其是 RPC,数据交互格式要转为 Protobuf,赋值是无法避免的 ...

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

    原文:SQL2005性能分析一些细节功能你是否有用到?(三) 继上篇: SQL2005性能分析一些细节功能你是否有用到?(二) 第一: SET STATISTICS PROFILE ON 当我们比较查 ...

  9. c#之冒泡排序的三种实现和性能分析

    冒泡排序算法是我们经常见到的尤其是子一些笔试题中. 下面和大家讨论c#中的冒泡排序,笔者提供了三种解决方案,并且会分析各自的性能优劣. 第一种估计大家都掌握的,使用数据交换来实现,这种就不多说了,园子 ...

随机推荐

  1. javascript 精确加减乘除

    最近一个项目中要使用 JS 实现自动计算的功能,本以为只是实现简单的加.减.乘.除就可以了,于是三下五除二做完了. 正当我窃喜的时候,发现问题了... 进行一些浮点数运算时,计算结果都是让我大跌眼镜啊 ...

  2. c#学习0217

    1 继承 继承 1 子类是否继承了父类的构造函数 答案:子类并没有继承父类的构造函数 但是子类或默认调用父类的无参数的构造函数 在子类中创建父类对象 这样子类才可以使用父类的成员 如果在父类中声明了有 ...

  3. SpringCloud学习笔记(15)----Spring Cloud Netflix之Hystrix Dashboard的使用

    1. 引入依赖 在前面几节中的消费者中添加pom依赖. <dependency> <groupId>org.springframework.cloud</groupId& ...

  4. 《Unix环境高级编程》读书笔记 第5章-标准I/O流

    1. 引言 标准I/O库由ISO C标准说明,由各个操作系统实现 标准I/O库处理很多细节,如缓冲区分配.以优化的块长度执行I/O等.这些处理使用户不必担心如何使用正确的块长度,这使得它便于用于使用, ...

  5. 理解ZBrush中的笔触

    笔触主要配合笔刷来使用,同样的笔刷搭配不同的笔触可以绘制出各种不同的效果.简言之,ZBrush 4R8就是提供了各种各样的笔触效果,例如,有模拟连贯笔触的效果,也有模拟喷枪喷洒的笔触效果. 下面简单认 ...

  6. java实现websocket 终极指南

    大概思路:  首先用户登陆  获取用户信息存储到httpsession中,然后客户端链接服务端websocket,首先HandshakeInterceptor这个拦截器会拦截请求 调用 beforeH ...

  7. SpringBoot实战(四)获取接口请求中的参数(@PathVariable,@RequestParam,@RequestBody)

    上一篇SpringBoot实战(二)Restful风格API接口中写了一个控制器,获取了前端请求的参数,现在我们就参数的获取与校验做一个介绍: 一:获取参数 SpringBoot提供的获取参数注解包括 ...

  8. idea 包的显示方式

    idea 可以通过点击Project的导航栏里的小齿轮里面有一个 Flatten packages 选项,将其勾上.就可以得到跟eclipse一样的包的显示方式. 没有设置默认是这样的 2018-06 ...

  9. 用Go语言写了一个电脑搜索文件的小东西

    package main import ( "bytes" "fmt" "os" "os/exec" "pat ...

  10. nginx 1.5 支持websocket

    proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set ...