Fork me on GitHub

Lucene 源码系列——索引文件的生成(二)之 doc&&pay&&pos

生成索引文件。tim、.tip、.doc、.pos、.pay 的流程图

图 1:

1.png

  我们继续介绍流程点 生成索引文件.doc、.pos、.pay

生成索引文件.doc、.pos、.pay 的流程图

图 2:

2.png

记录位置信息 position、payload、偏移信息 offset

图 3:

3.png

  当前 term 在一篇文档中的所有位置信息 position 以及偏移信息 offset 的起始位置是有序的,所以可以跟文档号一样(见索引文件的生成(一)关于数组 docDeltaBuffer 的介绍),分别使用差值存储到数组 posDeltaBuffer、offsetStartDeltaBuffer 中,而图 3 中其他数组,payloadLengthBuffer、payloadBytes、offsetLengthBuffer 则只能存储原始数据。另外要说的是,在处理的过程中,有些位置是不带有 payload 信息,那么对应 payloadLengthBuffer 中的数组元素为 0。

  这几个数组对应在索引文件.pos、.pay 中的位置如下所示:

图 4:

4.png

  另外图 4 中的索引文件。pay 中的字段 SumPayLength 描述的是当前 block 中 PayData 的的长度,在读取阶段用来确认 PayData 在索引文件。pay 中的数据区间。

是否需要生成 Block?

图 5:

5.png

  当处理 128 个当前 term 的位置信息 position 后,即 posDeltaBuffer 数组中的元素个数达到 128,那么就要生成三个 block:PackedPosBlock、PackedPayBlock、PackedOffsetBlock,即图 4 中的灰色标注的字段。

为什么要生成 PackedBlock

  当然是为了降低存储空间的使用量,至于能压缩率是多少,可以看 PackedInts 文章的介绍。

为什么选择 128 作为生成 PackedBlock 的阈值

  先给出源码中的注释:

    must be multiple of 64 because of PackedInts long-aligned encoding/decoding

  注释中要求阈值只要是 64 的倍数就行,目的是能字节对齐。因为在使用 PackedInts 实现压缩存储后的数据用 long 类型的数组存储,如果待处理的数据集(例如 posDeltaBuffer 数组)使用固定字节按位存储(见 PackedInts(一)),那么只要数据集中的数量是 64 的倍数,就能按照 64 对齐,即 long 类型数组中的每一个 long 中每一个 bit 位都是有效数据。至于为什么是 128,本人不做妄加猜测,目前没有弄明白。

处理完一篇文档后的工作

图 6:

6.png

  每处理完一篇包含当前 term 的文档,我们需要判断下我们目前处理的文档总数是否达到 128 篇,如果没有达到,那么该流程什么也不做,否则需要记录下面的信息:

  • lastBlockDocID:记录刚刚处理完的那篇文档的文档号
  • lastBlockPayFP:描述是处理完 128 篇文档后,在索引文件。pay 中的位置信息
  • lastBlockPosFP:描述是处理完 128 篇文档后,,在索引文件。pos 中的位置信息
  • lastBlockPosBufferUpto:在 posDeltaBuffer、payloadLengthBuffer、offsetStartDeltaBuffer、offsetLengthBuffer 数组中的数组下标值
  • lastBlockPayloadByteUpto:在 payloadBytes 数组中的数组下标值

上述的信息有什么

  上述信息作为参数用来生成跳表 SkipList,在介绍 SkipList 时再介绍这些参数,在这里我们只要知道这些信息的生成时机点。

执行处理后的收尾工作

图 7:

7.png

  当处理完所有包含当前 term 的文档后,我们需要 执行处理后的收尾工作。在前面的流程中,我们知道,每处理 128 篇文档或者 128 个位置信息 position 就会分别生成 Block,如果包含当前 term 的文档的数量或者位置信息总数不是 128 的倍数,那么到此流程,docDeltaBuffer、freqBuffer(见索引文件的生成(一))、posDeltaBuffer、payloadLengthBuffer、payloadBytes、offsetStartDeltaBuffer、offsetLengthBuffer 数组会有未处理的信息,而当前流程就是处理这些信息。

  对于 docDeltaBuffer、freqBuffer 数组中的信息,将会被存储到索引文件.doc 的 VIntBlocks 中,如下所示:

图 8:

8.png

  在 docDeltaBuffer、freqBuffer 数组中,当前 term 在一篇文档的文档号以及词频信息用图 8 中的一个 VIntBlock 来存储,VIntBlock 的个数跟 docDeltaBuffer、freqBuffer 数组的数组大小一致。

  这里存储 DocDelta、Freq 的有一个优化设计:组合存储(见倒排表(上)中关于组合存储的介绍)。

  对于 posDeltaBuffer、payloadLengthBuffer、payloadBytes、offsetStartDeltaBuffer、offsetLengthBuffer 数组中的信息,将会被存储到索引文件.pos 的 VIntBlocks 中,如下所示:

图 9:

9.png

结语

  至此,除了跳表 SkipList(下一篇文章介绍),生成索引文件.doc、.pos、.pay 的流程介绍完毕。


本文地址:https://www.6aiq.com/article/1586658833151
本文版权归作者和AIQ共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出