文章目录
一、前言
上一篇《Lucene倒排索引简述 之索引表》,已经对整个倒索引的结构进行大体介绍,并且详细介绍了索引表(TermsDictionary)的内容。同时还详细介绍了Lucene关于索引表的实现,相关文件结构详解,以及对索引表采用的数据结构进行剖析解读。
本篇博客将继续剖析Lucene关于倒排索引实现有另一个核心内容,倒排表(Postings)。我一直觉得Postings内容相对而言是比较简单,虽然内容很多,但是Lucene的官方文档讲得也非常详细了。如果对Lucene文档上的描述文件结构的表达方式不太熟悉的话,个人觉得可以参考前面的图自行画出文件结构示意图,或者直接在网络搜索相关的图,只要能整出索引文件的结构示意图,那么理解起来就应该不会太困难。
二、Postings编码
开始之前先介解Lucene在Postings采用了两个关键的编码格式,PackedBlock和VIntBlock。PackedBlock是在Lucene4.0
引入,带来向量化优化。
1. VIntBlock
VIntBlock是能够存储复合数据类型的数据结构,主要通过变长整型(Variable Integer)编码达到压缩的目的。此外VIntBlock还能够存储byte[]
,比如.pay用VIntBlock存储了payloads数据等。
值得一提的是,VIntBlock可以存储变长数据结构,如.doc用它存储DocID和TermFreq时,由于在特定条件下(TermFreq=1),Lucene会省略TermFreq以提高空间占用率。我知道Lucene用一个VInt来表示DocID,VInt则用每个Byte左边第一个Bit来表示是否需要读取顺续到下个Byte。也就是说一个VInt有效位是28bit
,这就说明VInt头部是有特殊含义的,因此Lucene只能在VInt最右边的一个bit下功夫。让VInt的右边第一Bit来表示是否有下个数据。
具体用法会在介绍.doc文件格式时介绍。
2. PackedBlock
PackedBlock只能存储单一结构,整数数组(Integer/Long)。这里主要是介绍PackedInts,即是将一个int[]打包成一个Block。PackedBlock只能能够存储固定长度的数组(Lucene规定其长度为128个元素),它压缩方式是将每个元素截断为预算的长度(length,单位是bit)压缩的。所以当长度length不是8的倍数时,会出现一个byte被多个元素占用。
PackedBlock需要把整个int[]的所有条目指定长度编码,所以PackedBlock只能选择int[]最大的数还来计算长度,否则会让大数失真。反过来,PackedBlock都选择64位,则会浪费空间,不能达到压缩的目的。
Lucene预先编译了64个PackedFormat编码器和解码器,即针对Long以内的每种长度都数据都有自己的解码和编码器,以提高编解码的性能。
PackedPosDeltaBlock与PackedDocDeltaBlock和PackedFreqBlock一样采用PackedInts结构,它能存储的信息实际上是很有限的,只能存储Int的数组。所以在PackedPosDeltaBlock的时候,只能存储position信息,在VIntBlock则会存储更多必要的信息,减少搜索时的IO操作。
这也是为什么需要将DocId和TermFreq拆分成PackedDocDeltaBlock和PackedFreqBlock两个Block存储的原因了。
定长是指PackedBlock限定了一个Block仅允许存储长度128的整型数组,而不是限定Block用多少个Bytes来存储编码后的结果。另外Block存储占用的大小,是按数组中最大那个数的有效bits长度来计算整个Block需要占用多大的Bytes数组的。也就是Block的每个数据的长度都是一样,都按最长bits的来算。
比如:(我们定义一个