索引文件的生成(十五)(Lucene 8.4.0)

  在前面的文章中,我们介绍了在Lucene7.5.0中索引文件.dvd&&.dvm的数据结构,从本篇文章开始介绍其生成索引文件.dvd&&.dvm的内容,注意的是,由于是基于Lucene8.4.0来描述其生成过程,故如果出现跟Lucene7.5.0中不一致的地方会另外指出,索引文件.dvd&&.dvm中的包含了下面几种类型:

  本篇文章从NumericDocValues开始介绍,建议先阅读下文章NumericDocValues,简单的了解NumericDocValues类型的DocValues的数据结构。

  在文章索引文件的生成(一)之doc&&pay&&pos中,简单的介绍了生成索引文件.dvd&&.dvm的时机点,为了能更好的理解其生成过程,会首先介绍下在生成索引文件之前,Lucene是如何收集每篇文档的NumericDocValues信息。

收集文档的NumericDocValues信息

  在源码中,通过NumericDocValuesWriter对象来实现文档的NumericDocValues信息的收集,并且具有相同域名的NumericDocValues信息使用同一个NumericDocValuesWriter对象来收集,例如下图中添加三篇文档:

图1:

  在使用NumericDocValuesField来生成NumericDocValues信息时要注意,在一篇文档中,仅能添加一条相同域名的NumericDocValuesField,但是能添加多条不同域名的NumericDocValuesField,如图1中,在文档0中添加了域名分别为"age"、"level"的NumericDocValuesField,如果我们在一篇文档中添加两个或以上相同域名的NumericDocValuesField,那么会抛出以下的异常:

图2:

图3:

  图3中抛出的异常对应的代码实际就是上文提到的NumericDocValuesWriter类中的:

图4:

  上文中说到相同域名的NumericDocValuesField对应的NumericDocValues信息使用同一个NumericDocValuesWriter收集,在收集的过程中,如果一篇文档中包含了两个或以上相同域名的NumericDocValuesField,如图4所示,docID <= lastDocId的条件就会成立,即抛出图3中的异常。

  在收集NumericDocValues信息的过程中,我们仅仅关心下面两个信息:

docId

  在源码中,使用DocsWithFieldSet对象收集文档号docId,使得在某个特殊条件(见下文流程点是否使用了FixedBitSet?的介绍)下,在索引阶段能更少的占用内存,在读取阶段有更好的读写性能

  我们通过介绍DocsWithFieldSet存储文档号的的流程来解释上述的内容:

图5:

  在介绍图5的流程之前,我们先介绍下图中几个变量:

文档号

图6:

  图5的流程描述了DocsWithFieldSet存储一个文档号的过程,所以流程图的准备数据为一个文档号。

文档号是否不大于已收集的文档号?

图7:

  通过比较当前处理的文档号docId跟lastDocId的值来判断文档号是否不大于已收集的文档号,如果判断为否,那么直接抛出异常:

图8:

  当前流程点判断的目的是想说明DocsWithFieldSet只处理从小到大有序的文档号集合。

FixedBitSet

图9:

  介绍图9的流程点之前,我们先说下DocsWithFieldSet存储文档号的两种方式:

某个特殊条件是什么?

  该特殊条件指的是DocsWithFieldSet处理的文档号集合中的文档号是从0开始有序递增的,这意味着段(段内的文档号是从0开始有序递增的)中每一篇文档中都包含某个域名的NumericDocValues信息。

  例如图1中,如果添加了三篇文档后生成了一个段,那么对于域名为"age"的NumericDocValues信息就满足上述的特殊条件,而对于域名为"level"的NumericDocValues信息,由于文档1中没有添加这个域,那么就不满足。

满足或者不满足特殊条件有什么不同

  如果满足特殊条件,意味着我们不需要存储每个文档号,只需要知道cost的值就行了,cost的默认值为0,每处理一个文档号,cost的值就执行+1操作,那么在读取文档号阶段我们就可以根据cost获得文档号集合区间,即[0, cost]。

  如果不满足特殊条件,那么只能通过FixedBitSet来存储每一个文档号。

  可见如果满足了特殊条件,在索引阶段,我们就不需要额外使用FixedBitSet对象来存储文档号,即上文中提到的在索引阶段能更少的占用内存;同时在读取阶段,我们只要顺序遍历0~cost的值就可以获取文档号,而不需要通过FixedBitSet来读取文档号(见文章工具类之FixedBitSet),即上文中提到的在读取阶段有更好的读写性能

  回到当前流程点的介绍,如果流程点是否使用了FixedBitSet?为否,说明之前处理的文档号集合是从0开始有序递增的,如果为是,那么只能通过FixedBitSet存储文档号,即执行流程点使用FixedBitSet存储文档号,接着在流程点是否新建FixedBitSet?的判断中,通过比较当前处理的文档号docId跟cost值来进行判断:

域值

  域值即NumericDocValuesField的域值,例如图1的文档0中,域名为"age"的域值为88,我们需要收集88这个域值。

  源码中使用了PackedLongValues来实现压缩存储,关于PackedLongValues的内容请参看文章PackedInts(一),本文不赘述,在后面的文章中,会再次介绍PackedLongValues的内容。

结语

  

点击下载附件