2.索引技术
索引是关系型数据库里的重要概念。总的来说,索引就是拿空间换时间。数据库技术和大数据技术会有一个融合的过程,除了前面讲到的B数索引、Hash索引等,还有倒排索引、MinMax索引、BitSet索引、MDK索引等。
大数据的核心是“大”,大数据索引和传统索引最主要的不同考虑点也是数据量的级别增大后索引本身也会变得很大。传统的B树索引是一个全局索引,数据量增大后,可能一台物理机的内存根本无法装下索引本身,每次插入之后,索引更新的代价会大到无法接受。索引本身的分布式需要充分考虑。
另外一个变化就是很多索引不再单独存储。有一种思路就是,数据本身以索引的形式存储下来,需要的时候才加载到内存中,而不是传统实现里将全部索引装载到内存中。
1)倒排索引
在一个未经处理的数据库中,一般以文档ID作为索引,以文档内容作为记录。而Inverted Index指的是将单词或记录作为索引,将文档ID作为记录,这样便可以方便地通过单词或记录查找到其所在的文档。
2)Lucene倒排索引原理
Lucene是一个高性能的Java全文检索工具包,它使用的是倒排文件索引结构。该结构及相应的生成算法如下。
(1)设有两篇文章1和2。
文章1的内容为:Tom lives in Guangzhou,I live in Guangzhoutoo。
文章2的内容为:He once lived in Shanghai。
(2)由于Lucene是基于关键词索引和查询的,所以首先要取得这两篇文章的关键词。通常的处理措施如下:
a. 我们现在拥有的是文章内容,即一个字符串,先要找出字符串中的所有单词,即分词。英文单词由于用空格分隔,所以比较好处理。中文单词是连在一起的,因而需要特殊的分词处理。
b. 文章中的“in”、“once”、“too”等词没有什么实际意义,中文中的“的”、“是”等字通常也无具体含义,这些不代表概念的词可以过滤掉。
c. 用户通常希望查“He”时能把含“he”、“HE”的文章也找出来,所以所有单词需要统一大小写。
d. 用户通常希望查“live”时能把含“lives”、“lived”的文章也找出来,所以需要把“lives”、“lived”还原成“live”。
e. 文章中的标点符号通常不表示某种概念,也可以过滤掉。
在Lucene中,以上措施由Analyzer类完成。经过处理后,文章1的所有关键词为:[tom][live] [guangzhou][i][live][guangzhou];文章2的所有关键词为:[he][live][shanghai]。
(3)有了关键词后,我们就可以建立倒排索引了。上面的对应关系是:“文章号”对“文章中所有关键词”。倒排索引把这个关系倒过来,变成:“关键词”对“拥有该关键词的所有文章号”。文章1、2经过倒排后变成:
关键词 文章号
Guangzhou 1
He 2
I 1
Live 1,2
Shanghai 2
Tom 1
通常仅知道关键词在哪些文章中出现还不够,还需要知道关键词在文章中的出现频率和出现位置。通常有两种位置:
字符位置,即记录该词是文章中第几个字符(优点是关键词亮显时定位快)。
关键词位置,即记录该词是文章中第几个关键词(优点是节约索引空间、词组查询快)。Lucene中记录的就是这种位置。
加上“出现频率”和“出现位置”信息后,索引结构变为:
关键词 文章号[出现频率] 出现位置
guangzhou 1[2] 3,6
he 2[1] 1
i 1[1] 4
live 1[2],2[1] 2,5,2
shanghai 2[1] 3
tom 1[1] 1
以live这一行为例说明一下该结构:live在文章1中出现了2次,在文章2中出现了1次,它的出现位置为“2,5,2”。这表示什么呢?我们需要结合文章号和出现频率来分析。文章1中出现了2次,那么“2,5”就表示live在文章1中出现的两个位置;在文章2中出现了一次,剩下的“2”就表示live是文章2中的第2个关键字。
以上就是Lucene索引结构中最核心的部分。我们注意到关键字是按字符顺序排列的(Lucene没有使用B树结构),因此,Lucene可以用二元搜索算法快速定位关键词。
实现时,Lucene将上面三列分别作为词典文件(TermDictionary)、频率文件(Frequencies)、位置文件(Positions)保存。其中词典文件不仅保存了每个关键词,还保存了指向频率文件和位置文件的指针,通过这些指针可以找到该关键字的频率信息和位置信息。
Lucene中使用了Field的概念,用于表达信息所在位置(如标题中、文章中、URL中)。在创建索引时,该Field信息也记录在词典文件中,每个关键词都有一个Field信息(因为每个关键字一定属于一个或多个Field)。
为了减小索引文件的大小,Lucene对索引使用了压缩技术。首先,对词典文件中的关键词进行压缩,关键词压缩为<前缀长度,后缀>。例如,当前词为“阿拉伯语”,上一个词为“阿拉伯”,那么“阿拉伯语”被压缩为<3,语>。其次,大量用到的是对数字的压缩,数字只保存与上一个值的差值(这样就可以减小数字的长度,进而减少保存该数字所需的字节数)。例如,当前文章号是16389(不压缩要用3字节保存),上一个文章号是16382,压缩后保存7(只用1字节)。
下面通过对该索引的查询来解释一下为什么要建立索引。
假设要查询单词“live”,Lucene先对词典进行二元查找,找到该词后,通过指向频率文件的指针读出所有文章号,然后返回结果。词典通常非常小,因而整个查询过程的时间是毫秒级的。
而用普通的顺序匹配算法,不创建索引,而是对所有文章的内容进行字符串匹配。这一过程将会相当缓慢,当文章数目很大时,所需时间往往是无法忍受的。
3)正向索引和倒排索引的联系与区别
正向索引是经过搜索引擎对页面文本分词、消噪、去重、提取关键词后,得到的能够反映页面主体内容的一个关键词组成的集合。同时记录每一个关键词在页面上的出现频率、出现次数、格式、位置。这样,每个页面都可以被记录为一个关键词的元组,其中包含每个关键词的词频、格式、位置等权重信息。
正向索引不能直接用于排名。如果只存在正向索引,那么排名程序需要扫描所有索引库中的文件,找出包含关键词的文件,再进行相关性计算,这样的计算量无法满足实时返回排名结果的要求。
所以搜索引擎会将正向索引数据仓库重新构造为倒排索引,把文件到关键词的映射转换为关键词到文件的映射。在倒排索引中,关键词是主键,每个关键词都对应一系列文件,这些文件中都出现了这个关键词。这样,当用户搜索某个关键词时,排序程序在倒排索引中定位到这个关键词,就可以立即找出所有包含这个关键词的文件。
本文选自《大数据架构详解:从数据获取到深度学习》10.6.1.2节。