Lucene 压缩算法 - Frame Of Reference 家族

·  阅读 156

初衷

由于近一年的工作跟搜索强相关,我根据业务订制了Go版本的搜索服务,支持前缀查找,单词纠错,单词分词,命中范围计算,业务上仅需要支持Union类型的query。当然,我们的最最最基础的召回系统还是ElasticSearch,除了贵点没有别的毛病。他山之石可以攻玉,接下来会系统了解 Lucene 的优化细节,希望能对搜索服务的优化升级有启发。

倒排链

众所周知,倒排索引是搜索系统中最重要的部分之一。倒排索引由单词(Term)和对应的倒排链构成 (Posting List)。倒排链包含一连串有序的DocID。为了永久化存储索引和减少不必要的内存占用,Lucene会将这些倒排列表存储到硬盘上。在写入硬盘的时候,为了减小倒排文件的大小,Lucene采用了Frame Of Reference来对数组类型的数据进行压缩。

Frame Of Reference (FOR)

倒排链的一个非常有用的特性就是DocID按单调递增有序排列。这就可以使用增量编码进行压缩。 例如,如果倒排链上的DocID是[73, 300, 302, 332, 343, 372],则DocID之间的增量将是[73, 227, 2, 30, 11, 29]。具体计算逻辑如下:

  • 第1个位置 DocID 是73,前面没有数字,因此增量是0.
  • 第2个位置 DocID 是300,前面是73,因此 300-73=227.
  • 第3个位置 DocID 是302,前面是300,因此 302-300=2.
  • 第4个位置 DocID 是332,前面是302,因此 332-302=30.
  • 第5个位置 DocID 是343,前面是332,因此 343-332=11.
  • 第6个位置 DocID 是372,前面是343,因此 372-343=29.

在这个例子中,值得注意的是,所有增量都在 0 到 255 之间,因此每个值只需要一个字节就能存储,原来每个值需要两字节,压缩率达50%。这是 Lucene 用于在磁盘上编码倒排索引的技术:倒排链先进行增量编码然后分块,每块包含256个文档,最后位打包压缩每个块:Lucene 计算在块中存储增量的最大位数,将此信息添加到块头,使用此位数对块的所有增量进行编码。

Exmaple: block size = 3 的例子。如下图所示,Step 1 计算增量数组,Step 2 按每3个数字分块,Step 3计算每块中存储最大数字所需要的比特数,并添加到块头。如 [73,227,2] 最大数是 227 需要 8 bits,总共需要83=24 bits=3 bytes。[30,11,29] 最大数是 30,需要 5 bits, 35=15 bits=2 bytes。

for.png

Patched Frame Of Reference (PFOR)

但是,如果遇到一些明显的离群点(Outlier),压缩性能就急剧下滑。例如增量数组是 [1,2,293,3,4,5], FOR 为了存储293这个最大值必须使用 9 bits 来编码,尽管除了 293 之外其余每个数字只用 3 bits。这将极大的降低压缩效率。

因此, Patched Frame Of Reference 是为了解决较大异常值而使得b过大提出来的一种改进算法,b是指存储最大值所需要的位数。 PFOR 为大多数正常值(90%)选择固定位数b来存储(值小于 2^b),不能用b位表示的值称为异常值会被存储到另外的异常数组。

因此,PFOR存在三个部分,头(Header), 正常值数组 (Normal Data Section),异常值数组(Exception Data Section)。正常值数组中其异常值所占的b位用来存储下一个异常值的位置。真实的异常值存储在异常值数组中。当b位无法表示其异常值的位置间隔后,需要增大b的值,这样会使得压缩率下降。在Header部分存放三个值。第一个是正常值每位所需要的b,占一个字节。后32位存储两个指针,第一个指针指向第一个异常值出现的位置。第二个指针指向第一个异常值存储的位置。

例如:增量数组[1,2,293,3,4,5,301],其中有两个异常值 '293' 和 '301'。设定b=3,那么在293这个位置上,存储和异常值301的位置距离4.而301是最后一个异常值,其值是0.

image.png

NewPFD and OptPFD

除了上述方法之外,还有很多其他针对于 FOR 的改进方法,比如 NewPFD, OptPFD,FastPFOR 等等,包括对压缩率和解压缩速度的优化等,其思路主要还是针对于异常值的处理优化,有兴趣的同学还可以继续研究。

引用

1. Blog: Lucene-performance-with-pfordelta-codec

2. Blog: Frame-of-reference-and-roaring-bitmap

3. Paper: Compressing Inverted Index Using Optimal FastPFOR

4. Blog: Lucenes New Block Postings Format

分类:
后端
标签:
分类:
后端
标签:
收藏成功!
已添加到「」, 点击更改