索引文件的读取(七)之tim&&tip(Lucene 8.4.0)
Lu Xugang Lv6

  本篇文章开始介绍索引文件tim&&tip的读取,通过TermRangeQuery的例子来介绍如何从索引文件.tim&&.tip中获取满足查询条件的所有term。

  为了便于介绍,使用了文章Automaton(二)中提供的例子:

图1:

  结合图1的例子,获取满足查询条件(第79行代码)的所有term的过程可以简单的用一句话来描述:根据域名"content",从索引文件.tim&&.tip中获取该域对应的term集合,随后遍历集合中的每一个term,使用DFA(见文章Automaton(二))筛选出满足条件的term,流程图如下所示:

获取满足TermRangeQuery查询条件的term集合的流程图

图2:

BlockTreeTermsReader

图3:

  在介绍BlockTreeTermsReader的概念或者说包含的信息之前,我们先简单的介绍下该对象的生成时机点,在生成StandardDirectoryReader对象期间,会生成SegmentReader对象,该对象中的FieldsProducer信息描述了索引文件tim&&tip索引文件doc索引文件pos&&pay中所有域的索引信息,而BlockTreeTermsReader作为FieldsProducer信息的成员之一,作为索引文件tim&&tip在内存中的描述方式。

  BlockTreeTermsReader对象中,最重要也是我们唯一关心的信息就是一个名为fieldMap的Map容器,定义如下所示:

图4:

  fieldMap容器中,key为域名,value,即FieldReader对象,为该域名对应一些信息,在生成BlockTreeTermsReader对象的过程中,所有域的信息将用FieldReader对象来描述,接着我们介绍哪些信息会被用于生成FieldReader对象中。

获取索引文件.tip&&tim中数据的起始读取位置

  通过读取索引文件.tip&&tim来获得生成FieldReader对象的信息, 两个索引文件都是通过各自的DirOffset字段来获取数据的起始读取位置,先给出这两个索引文件的数据结构:

图5:

  在当前版本Lucene 8.4.0中,由于DirOffset跟Footer字段分别占用固定的8、16个字节,共24个字节,故这两个索引文件可以从后往前定位到DirOffset字段的起始读取位置,然后根据DirOffset字段的值再获取数据的读取位置,最后相关信息写入到FieldReader中,如图4所示。

FieldSummary

  对于索引文件.tim,它包含的FieldSummary字段中包含了所有域的一些MetaData,如下所示:

图6:

  图6中,除了FieldNumber字段,其他字段都会被写入到FIeldReader对象中,简单了解这些字段的介绍可以看文章索引文件之tim&&tip,详细了解这些字段的生成过程可以阅读文章索引文件的生成(五)之tim&&tip索引文件的生成(六)之tim&&tip以及索引文件的生成(七)之tim&&tip

IndexStartFP

  对于索引文件.tip,IndexStartFP描述了每个域对应的FSTIndex字段在索引文件.tip中的起始读取位置,如下所示:

图7:

  FSTIndex字段描述了该域的第一个读取NodeBlock对应的FST信息,介绍见文章索引文件的生成(七)之tim&&tip

FieldReader

  在生成FieldReader对象的过程中,我们着重关心FST的导入模式(load Mode),目前有两种模式可选择:

  • off-heap:该模式可以简单理解为使用时载入,载入动作为磁盘I/O操作,只将需要使用的信息读入到内存中
  • on-heap:该模式为一次性载入,直接将所有信息读入内存

  在文章索引文件的生成(六)之tim&&tip中我们说到,在合并了PendingBlock之后,会生成一个FST,其中第一个PendingBlock中的prefix将作为FST的inputValue,合并的信息作为FST的outValue。

  通过图7中的FSTIndex中的信息来生成FST,该字段的结构如下所示,各个字段的含义在文章索引文件的生成(七)之tim&&tip已经介绍,不赘述:

图8:

  图8中bytes字段记录了FST的主要信息,意味着这个字段占用的字节是最大的,它包含的信息就是在文章FST(一)中,文章结尾最终生成的current[ ]数组。

off-heap

  当导入模式为off-heap时,只会保存图8中的offset的值,它描述了bytes的读取位置,由于当前流程点处在BlockTreeTermsReader,还没有到查询阶段,目前是不知道查询条件中会对哪个域名进行查询,故使用off-heap后就不用载入所有域的FST的主要信息,在图2的流程点获取迭代器IntersectTermsEnum根据查询条件获取某个域的迭代器(查询阶段)时才会真正的去磁盘读取该域的FST的主要信息,换个说法就是在当前流程点只是获取所有域的FST主要信息的一个"句柄"跟文件指针(offset),并没有获得完整的FST信息。

on-heap

  当导入模式为on-heap时,则会从磁盘读取所有域的FST的主要信息,即读取所有域的FSTIndex字段的bytes字段。

  上述的导入模式在Lucene 8.0.0才加入,在之前的版本中,只使用on-heap,该issue地址:https://issues.apache.org/jira/browse/LUCENE-8635?jql=project %3D LUCENE AND text ~ "FST BytesStore"

  在使用on-heap一次性载入所有域的FST信息时还会根据FST信息的大小使用不用数据结构存储,原因在于Java中的byte[ ]数组大小存在上限,Lucene通过图8中的numBytes字段判断出bytes字段的大小,分别使用下面两个数据结构存储:

  • byte[ ]数组:如果numBytes小于等于1GB,那么直接使用byte[ ]数组存储

图9:

  • BytesStore对象:如果numBytes大于1GB,那么使用该数据结构
    • 该对象中使用了多个byte[ ] 存储bytes字段的信息,定义如下:

图10:

  该对象中定义了一个链表:

图11:

结语

  至此,图1的流程点BlockTreeTermsReader介绍结束,基于篇幅,剩余的内容将在下一篇文章中展开。

点击下载附件

 Comments