本文节选自《这就是搜索引擎:核心技术详解》第三章


3.4建立索引

正如前述章节所述,索引结构如果建立好了,可以增加搜索的速度,那么给定一个文档集合,索引是如何建立起来的呢?建立索引的方式有很多种,本节叙述比较实用的三种建立索引的方法。

3.4.1两遍文档遍历法(2-Pass In-Memory Inversion)

 

顾名思义,此种方法需要对文档集合进行两遍扫描,图3-11是这种方法的示意图。值得注意的一点是:此种方法完全是在内存里完成索引的创建过程的,而另外两种方法则是通过内存和磁盘相互配合来完成索引建立任务。

图3-11 两遍文档遍历法

第一遍文档遍历

在第一遍扫描文档集合时,该方法并没有立即开始建立索引,而是收集一些全局的统计信息。比如文档集合包含的文档个数N,文档集合内所包含的不同单词个数M,每个单词在多少个文档中出现过的信息DF。将所有单词对应的DF值全部相加,就可以知道建立最终索引所需内存大小是多少,因为一个单词对应的DF值如果是10,说明有10个文档包含这个单词,那么这个单词对应的倒排列表应该包含10项内容,每一项记载某个文档的文档ID和单词在该文档对应的出现次数TF。

在获得了上述三类信息后,就可以知道最终索引的大小,于是在内存中分配足够大的空间,用来存储倒排索引内容。如图3-11所示,在内存中可以开辟连续存储区域,因为第一遍扫描已经获得了每个单词的DF信息,所以将连续存储区划分成不同大小的“片段”,词典内某个单词根据自己对应的DF信息,可以通过指针,指向属于自己的内存“片段”的起始位置和终结位置,将来在第二遍扫描时,这个单词对应的倒排列表信息会被填充进入这个片段中。

综上所述,第一遍扫描的主要目的是获得一些统计信息,并根据统计信息分配内存等资源,同时建立好了单词相对应倒排列表在内存中的位置信息,即主要做些资源准备工作。

第二遍文档遍历

在第二遍扫描的时候,开始真正建立每个单词的倒排列表信息,即对于某个单词来说,获得包含这个单词的每个文档的文档ID,以及这个单词在文档中的出现次数TF,这样就可以不断填充第一遍扫描所分配的内存空间。当第二遍扫描结束的时候,分配的内存空间正好被填充满,而每个单词用指针所指向的内存区域“片段”,其起始位置和结束位置之间的数据就是这个单词对应的倒排列表。

经过两遍扫描完成索引建立后,即可将内存的倒排列表和词典信息写入磁盘,这样就完成了建立索引的过程。从上述流程可以看出,索引的构建完全是在内存中完成的,这就要求内存的大小一定要足够大,否则如果文档集合太大时,内存未必能够满足需求。

从另外一个角度看,在建立索引的过程中,从磁盘读取文档并解析文档基本是最消耗时间的一个步骤,而两遍扫描法因为要对文档集合进行两遍遍历,所以从速度上不占优势,在实际中采用这种方法的系统并不常见。而下面介绍的两种方法都是对文档集合进行一遍扫描,所以在速度方面明显占优。

3.4.2排序法(Sort-basedInversion)

两遍遍历法在建立索引过程中,对内存的消耗要求较高,不同的文档集合包含文档数量大小不同,其所需内存大小是不确定的。当文档集合非常大时,可能因内存不够,导致无法建立索引。排序法对此做出了改进,该方法在建立索引的过程中,始终在内存中分配固定大小的内存,用来存放词典信息和索引的中间结果,当分配的内存被消耗光的时候,把中间结果写入磁盘,清空内存里中间结果所占内存,以用作下一轮存放索引中间结果的存储区。这种方法由于只需要固定大小的内存,所以可以对任意大小的文档集合建立索引(参考图3-12)。

图3-12 排序法

中间结果内存排序

图3-12是排序法在内存中建立索引中间结果的示意图。读入文档后,给文档进行编号,赋予唯一的文档ID,并对文档内容解析。对于文档中出现的单词,通过查词典将单词转换为对应的单词ID,如果词典中没有这个单词,说明是第一次碰到,则赋予单词以唯一的单词ID并插入词典中。在完成了由单词映射为单词ID过程之后,可以对该文档内每个单词建立一个(单词ID,文档ID,单词频率)三元组,这个三元组就是单词对应文档的倒排列表项,将这个三元组追加进入中间结果存储区末尾。如果文档内的所有单词都经过如此处理,形成三元组序列的形式,则该文档被处理完成,开始依次序处理下一文档,过程与此类似。

随着新的文档不断被处理完成,存储三元组集合的中间结果所占用的内存会越来越多,词典里包含的新单词也越来越多,当分配的内存定额被占满时,该方法对三元组中间结果进行排序。排序的原则是:主键是单词ID,即首先要按照单词ID由小到大排序。次键是文档ID,即在相同单词ID的情况下,按照文档ID由小到大排序。通过以上方式,三元组变成有序形式。为了腾出内存空间,将排好序的三元组写入磁盘临时文件中,这样就空出内存来进行后续文档的处理。这里需要注意的是:在建立索引过程中,词典是一直存储在内存中的,每次清空内存只是将中间结果写入磁盘。随着处理文档的加大,词典占用的内存会逐渐增加,由于分配内存是固定大小,而词典占用内存会越来越大,也就是说,越往后,可用来存储三元组的空间是越来越少的。

之所以要对中间结果进行排序,主要是为了方便后续的处理。因为每一轮处理都会在磁盘产生一个对应的中间结果文件,当所有文档处理完成后,在磁盘会有多个中间结果文件,为了产生最终的索引,需要对这些中间结果文件合并。图3-13是如何对中间结果进行合并的示意图。

图3-13 中间文件合并

合并中间结果

如图3-13所示,在合并中间结果的过程中,系统为每个中间结果文件在内存中开辟一个数据缓冲区,用来存放文件的部分数据。因为在形成中间结果文件前,已经按照单词ID和文档ID进行了排序,所以进入缓冲区的数据已经是有序的。合并过程中,将不同缓冲区中包含的同一个单词ID的三元组进行合并,如果某个单词ID的所有三元组全部合并完成,说明这个单词的倒排列表已经构建完成,则将其写入最终索引中,同时将各个缓冲区中对应这个单词ID的三元组内容清空,这样缓冲区就可以继续从中间结果文件中读入后续的三元组来进行下一个单词的三元组合并。当所有中间结果文件都依次被读入缓冲区,在合并完成后,就形成了最终的索引文件。

3.4.3归并法(Merge-basedInversion)

“排序法”分配固定大小内存来建立索引,所以无论要建索引的文档集合有多大,都可以通过这种方法完成。但是如上所述,在分配的内存定额被消耗光时,

“排序法”只是将中间结果写入磁盘,而词典信息一直在内存中进行维护,随着处理文档越来越多,词典里包含的词典项越来越多,所以占用内存越来越大,导致后期中间结果可用内存越来越少。“归并法”对此做出了改进,即每次将内存中数据写入磁盘时,包括词典在内的所有中间结果信息都被写入磁盘,这样内存所有内容都可以被清空,后续建立索引可以使用全部的定额内存。

图3-14是“归并法”的示意图。其整体流程和排序法大致相同,也是分为两个大的阶段,首先在内存里维护中间结果,当内存占满后,将内存数据写入磁盘临时文件,第二阶段对临时文件进行归并形成最终索引。

图3-14 归并法

尽管整体流程看上去和排序法大致相同,但是在具体实现方式上有较大差异。

首先,“排序法”在内存中存放的是词典信息和三元组数据,在建立索引过程中,词典和三元组数据并没有直接的联系,词典只是为了将单词映射为单词ID。而“归并法”则是在内存中建立一个完整的内存索引结构,相当于对目前处理的文档子集单独在内存建立起了一整套倒排索引,和最终的索引相比,其结构和形式是相同的,区别只是这个索引只是部分文档的索引而非全部文档的索引。

其次,在将中间结果写入磁盘临时文件时,“归并法”会将整个内存的倒排索引写入临时文件,对于某个单词的倒排列表,在写入磁盘文件时,将词典项放在列表最前端,之后跟随相应的倒排列表,这样依次将单词和对应的倒排列表写入磁盘文件,随后彻底清空所占内存。而“排序法”如上节所述,只是将三元组数据排序后写入磁盘临时文件,词典作为一个映射表一直存储在内存中。

在最后的临时文件合并为最终索引过程中,两者也有差异。“排序法”因为保存的是有序三元组信息,所以在合并时,是对同一单词的三元组依次进行合并;而“归并法”的临时文件则是每个单词对应的部分倒排列表,所以在合并时针对每个单词的倒排列表进行合并,形成这个单词的最终倒排列表。另外,“归并法”在最后的合并过程中形成最终的词典信息。

搜索引擎索引之如何建立索引相关推荐

  1. 什么是索引?为什么要建立索引?并举例说明.(以某一具体的DBMS为例)

    什么是索引?为什么要建立索引?并举例说明.(以某一具体的DBMS为例) 悬赏分:100 - 解决时间:2008-7-7 16:40 什么是聚簇索引?为什么要建立聚簇索引?并举例说明. 希望可以一起回答 ...

  2. mysql 织梦 索引_Mysql索引详解 建立索引的优势劣势以及索引规范

    索引是什么 索引(index)是帮助MySQL高效获取数据的数据结构 如果没有特别指明,都是指的是B树索引(多路搜索树,并不一定是二叉树)结构组织的索引 建立索引的优势和劣势 优势 提高数据检索的效率 ...

  3. oracle加强制索引,Oracle中建立索引并强制优化器使用

    当WHERE子句对某一列使用函数时,除非利用这个简单的技术强制索引,否则Oracle优化器不能在查询中使用索引. 通常情况下,如果在WHERE子句中不使用诸如UPPER.REPLACE 或SUBSTR ...

  4. oracle 数据库如何建立索引 如何用索引?

    我现在有一个SQL语句  SELECT FAQID, FAQNAME, TYPE, CREATOR, TOQUESTION, SUPERCODE FROM T_KBS_FAQ t where TYPE ...

  5. 数据库建立索引怎么利用索引查询

    数据库建立索引怎么利用索引查询? 精选 1.合理使用索引 索引是数据库中重要的数据结构,它的根本目的就是为了提高查询效率.现在大多数的数据库产品都采用IBM最先提出的ISAM索引结构. 索引的使用要恰 ...

  6. (转)Mysql哪些字段适合建立索引

    工作中处理数据时,发现某个表的数据达近亿条,所以要为表建索引提高查询性能,以下两篇文章总结的很好,记录一下,以备后用. 数据库建立索引常用的规则如下: 1.表的主键.外键必须有索引:  2.数据量超过 ...

  7. MySQL索引优化:索引失效以及不适合建立索引的场景

    引言: 索引是有双面性的,合理的建立索引可以提高数据库的效率.但是如果没有合理的构建索引和使用索引,可能会导致索引失效或者影响数据库性能,本文主要讨论的是索引失效以及不适合建立索引的场景 结论:具体案 ...

  8. mysql 唯一索引_MySQL学会用索引,让你数据库的查询速度起飞

    MySQL索引的建立对于MySQL的高效运行是很重要的,索引可以大大提高MySQL的检索速度.打个比方,如果合理的设计且使用索引的MySQL是一辆兰博基尼的话,那么没有设计和使用索引的MySQL就是一 ...

  9. 计算机语言中索引什么意思,算法索引

    索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息.算法索引是指算法集成包或API中算法调用接口按照某种规则排序.算法索引有利于对有关算法和调用.算法索引一般是 ...

最新文章

  1. tensorflow LSTM + CTC实现端到端OCR
  2. Inline Hook
  3. Java集合(二):List列表
  4. Spring Mvc + Spring + Mybatis3 搭建Web工程详解
  5. 计算机网络—时延相关真题练习(三)
  6. Struts+Hibernate系列教材 (一)- 整合Struts和Hibernate教程
  7. EXCEL常用查询函数?查询函数的妙用
  8. 将经纬度转换为以度为单位的xy坐标
  9. 说说几种常用的前端缓存
  10. 51单片机数码管表白
  11. td设置虚线dotted框,在chrome下有时会出现实线bug
  12. Vue-----table 控件自动勾选全选框2 与tab控件组合使用
  13. 酷我CEO雷鸣:差异化服务是制胜关键
  14. 白盒测试简介与逻辑覆盖
  15. C#【EF Core框架】使用乐观锁处理并发冲突
  16. MPC5744P-CAN模块
  17. GSON解析JSON保存到数据库
  18. 如何压缩打包图片文件?照片如何打包压缩?
  19. python编程无师自通pdf_Python编程无师自通:专业程序员的养成 PDF下载
  20. 条形码每一位数字的含义是什么

热门文章

  1. 替换系统wsock32.dll,实现封包拦截
  2. 作为程序员,这些实用工具你必须要知道!
  3. Jedis对redis的操作详解
  4. 面试官:String的最大长度是多少?
  5. 分布式锁(Redisson)-从零开始,深入理解与不断优化
  6. 朱明亮:参与开源软件让业余时间更有意义
  7. 为技术匠人打call!用匠人精神,打造技术文化
  8. 从并发模型看 Go 的语言设计
  9. 腾讯正式加入OCP阵营,拥抱全球开源生态圈
  10. python 第三方库