一)单词-文档矩阵
通常检索的场景是:给定几个关键词,找出包含关键词的文档。
怎么快速找到包含某个关键词的文档就成为搜索的关键。这里我们借助单词-文档矩阵模型,通过这个模型我们可以很方便知道某篇文档包含哪些单词,某个单词被哪些文档所包含。
搜索引擎的索引其实就是实现单词-文档矩阵的具体数据结构,具体可以包括:倒排索引、签名文件、后缀树等。常见的当然就是倒排索引了,lucene也是基于倒排索引实现的。
二)倒排索引
1.倒排索引的组成
倒排索引通常有词汇表和记录表组成。
词汇表:文档集合中所包含的不同单词的集合。
记录表:对于词汇表中的每一个单词,包含这个单词的文档编号构成的一个列表(有可能还会保存些其他信息,例如单词在文档中的位置信息)。
2.使用倒排索引(检索)
这样的结构怎么用于检索?
通常我们将词汇表和记录表分开存储,词汇表文件中包含每个单词指向记录表文件的指针。先在词汇表中找到需要检索的单词,然后取出对应的记录表。如果查询中只有一个单词,取出记录表即可,如果查询中包含多个单词,取出多个记录表还需要做合并(交集或者并集)。
死做结果肯定可以出来,但是这里效率必须强调。怎么快速的找到单词然后获取记录表内容呢?如果你的词表不是很大(主存可以容纳得下),那么排序数组、b树、trie树、散列都是可选的数据结构,散列速度肯定毋庸置疑,b树、trie树可以处理前缀查询和范围查询。lucene中不考虑将整个词表存入主存,他的词表文件分成两个,类似两层跳跃表,中间采用二分查找。
3.建立索引
一般分成三种方式:
3.1.两遍文档遍历方法(2-Pass In-Memory Inversion)
这种方式完全是依赖内存的。第一遍扫描所有文档,计算每个单词的tf,然后计算所有单词tf的总和,这个总和值直接关系到最终索引所需的内存大小。在内存中,建立有序数组用来存储单词,建立一块连续的存储空间(由tf总和控制)用来存储倒排索引,并且每个单词通过“指针”指向其对应的倒排列表其实位置。第二遍扫描就是建立每个单词的倒排列表信息。当所有文档扫面结束后,再以某种方式存储起来。
这种方式的缺点是:1)两遍扫描,效率低下;2)完全依赖内存,对规模大点的文档集无能为力;3)不支持动态更新索引。
3.2.排序法(Sort-based Inversion)
为了解决两遍文档遍历方法的缺陷,排序法只在内存中开辟固定大小的空间用来建立索引,当空间消耗光时将其写入磁盘,然后清空内存空间对其他文档进行索引,直至索引完全部文档。排序法将单词全部存储在内存中,并且不停收集倒排项(倒排项包括:单词id,文档id,单词频率等信息),当空间消耗光时,对所有的倒排项按照单词id、文档id进行排序,排序好后将其写入临时文件。此时清空倒排项所占用的内存空间,然后重复上述工作。注意单词id是事先排好序的。在一开始,我们不会将全部的单词加载进入内存,是在遍历的过程中文档中出现了才加入内存。因此随着遍历,单词所占用的空间越来越大,倒排项所占用的空间越来越小,也就是说越到后面一次遍历的文档数量就越少,临时文件也就越小。
当遍历完整个文档集后,我们便得到了一堆的临时文件(存储了倒排项),接下来就要将这些文件进行“整合”。由于这些倒排项都排好序了,因此这个工作相对简单,就是将同一个单词的倒排项进行合并。
3.3.归并法(Merge-based Inversion)
排序法最致命的一个问题就是将全部单词放到内存里,如果单词量特别大,以至于内存放不下那就望尘莫及了!归并法可以很好的解决这个问题,归并法也在内存中开辟一块固定大小的空间,对于一篇文档就将其转换成标准的内存倒排索引结构,假设在内存空间的限制下,将n篇文档建立好了单独的内存倒排索引结构,然后进行合并形成一个索引段,写入磁盘,清空内存。这里的内存索引、索引段只是在文档数量规模上有所区别,结构上完全一致。在这个过程中同样需要对单词进行排序,方便后期的合并。
当遍历完整个文档集合后,我们同样得到了一堆临时文件(索引段),和排序法类似,我们将同一单词的倒排列表进行合并即可。
lucene采用的就是这种方式。
4.动态索引
索引建立完成后,如果后期不需要对其进行调整(增、删),那么称之为静态索引,反之为动态索引。
动态索引又将牵涉到实时索引(实时检索)等问题,这里不展开描述实时的问题。
动态索引包含三个部分:磁盘索引文件、内存临时索引、已删除文档列表。
内存临时索引和归并法中的内存索引一个意思,将新加的文档临时放在内存中。以删除文档列表记录删除文档的id。检索时同时查询磁盘索引文件和内存临时索引,然后通过已删除文档列表进行过滤。当内存临时索引达到一定阈值时将其合并到磁盘索引文件。
5.索引更新策略
索引更新主要包括完全重新更新(Complete Re-build)和增量更新(再合并策略Re-Merge、原地更新策略In-Place)。lucene中使用的是Re-Merge策略。
重点:
1.词汇表的存储和查询(数据结构)
排序数组、b树、trie树、散列