Press "Enter" to skip to content

索引文件之kdd&kdi&kdm

本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.

kdd&kdi&kdm (Lucene 8.6.0)

 

从Lucene8.6.0开始,用于存储点数据(point value)的索引文件由原先的两个 索引文件dim&&dii ,改为三个索引文件kdd&kdi&kdm。由于生成kdd&kdi&kdm的过程基本上没有太大的变动,并且索引文件的数据结构中的字段也变化不大。故本文不会再详细介绍每一个字段的含义,即阅读本章前,最好先看下文章 索引文件dim&&dii 的数据结构,以及 索引文件dim&&dii的生成过程 以及 索引文件dim&&dii的读取过程 的系列文章,使得能理解优化的目的。当然在下文中,会结合一些 issues 来简单的叙述下优化的目的。

 

再次强调下,先阅读 索引文件dim&&dii的生成过程 以及 索引文件dim&&dii的读取过程 的系列文章,因为开始下笔写这篇文章的时候,我几乎忘光了之前写的这些东东,也就是相当于一片空白的重新复习了下这些系列文章, 发现看完后很容易的理解了(看来我的写作表达能力还阔以),哈哈:grin:。

 

索引文件的数据结构

 

我们先直接分别给出单个点数据域、多个点数据域的数据结构:

 

单个点数据域的数据结构

 

图1:

 

 

多个点数据域的数据结构

 

图2:

 

 

源码中对于这三个索引文件的简单描述:

 

图3:

 

 

图3中 红框 标注.kdm应该是.kdd文件,可惜的是直到目前最新版本的Lucene 8.6.3,这里的书写错误依旧未被修正。

索引文件.kdm(meta)中存储的是元数据,即描述点数据域的数据的信息,例如点数据的维度、每个维度占用的字节数等等,这些元数据存储在图1的Index字段中,下文中会进一步介绍该字段。
索引文件.kdi(index)中存储的是内部节点的数据
索引文件.kdd(data)描述的是叶子节点的数据

点数据的信息使用了Bkd-tree的树形结构存储,可以阅读文章 Bkd-Tree 简单了解下bkd的概念。

 

为了便于介绍,我们以单个点数据域的数据结构来展开介绍。

 

索引文件.kdd

 

图4:

 

 

索引文件.kdd中存储了叶子节点的数据,其中字段LeafNodeData中包含的内容跟索引文件.dim中的LeafNodeData是相同的,如下图所示,详细的字段介绍见文章 索引文件dim&&dii ,这里不赘述。

 

图5:

 

 

索引文件.kdi

 

图6:

 

 

索引文件.kdi中存储了内部节点的数据,其中字段PackedIndexValue中包含的内容跟索引文件.dim中的PackedIndexValue是相同的,如下图所示,详细的字段介绍见文章 索引文件dim&&dii ,这里不赘述。

 

图7:

 

 

索引文件.kdm

 

图8:

 

 

索引文件.kdm中存储的是元数据,即描述点数据域的数据的信息,图8中,除了 红框 标注的几个字段,其他字段跟索引文件.dim中的BKD中的字段是相同的,如下图所示,详细的字段介绍见文章 索引文件dim&&dii ,这里不赘述。

 

图9:

 

 

我们接着介绍图9中未被标注 红框 的字段。

 

FieldNumber

 

该字段描述的是域的编号,在Lucene 8.6.0之前,该字段存储在索引文件.dii中,如下所示:

 

图10:

 

 

图10中,IndexFP字段用来指向图9中BKD的起始读取位置,该字段的作用相当于在Lucene8.6.0中的DataStartFP、IndexStartFP,见下文介绍。

 

-1

 

该字段会写入一个数值类型的固定值-1,在读取索引文件.kdm期间,它作为一个结束标志位用来描述所有域的FieldNumber跟Index字段信息已经被读取结束。

 

结合图2中多个点数据域的索引文件.kdm,由于每个域的FieldNumber、Index字段占用的字节数量是相同的,所以在读取阶段,只要按照固定的长度读取字节流即可,当读取到值为-1时,说明读取结束。这块内容在类 Lucene86PointsReader 初始化Reader时读取,由于代码比较简单,故直接给出:

 

图11:

 

 

图11中,第87行代码会读取索引文件.kdm的FieldNumber字段;第93行代码将会读取Index字段,字段中的数据将用于生成BKDReader,下文中还会进一步展开;第88行代码会判断是否读取了-1,如果满足那幺就跳出while循环,然后继续执行第96、97行的代码,即分别读取索引文件.kdm中的KdiFileLength、KddFileLength字段。

 

KdiFileLength、KddFileLength

 

这两个字段分别描述了索引文件.kdi、索引文件.kdd的文件长度,在读取阶段通过长度来检查这两个文件是否为合法的。同样的,检查的代码也相对简单,故直接给出:

 

图12:

 

 

KdiFileLength、KddFileLength的值将作为参数expectedLength传入到图12的方法中,其中in.length()方法描述了某个索引文件的长度,通过in.length()跟expectedLength的长度比较,判断索引文件是否合法的。

 

DataStartFP、IndexStartFP

 

这两个字段分别描述了某个点数据域的叶子节点、内部节点数据在索引文件.kdd、kdi中起始读取位置:

 

图13:

 

 

优化目的

 

通过上文介绍大家可能会发现,用于存储点数据的索引文件在Lucene8.6.0中的主要变动就是将内部节点跟叶子节点的信息从索引文件.dim中进行了分离,这里做的目的是出于什幺考虑呢?作者的详细解释见原文地址: https://issues.apache.org/jira/browse/LUCENE-9148

 

其优化的目的主要有以下两点:

可以让用户充分利用MmapDirectory的preload功能,使得可以将叶子节点、内部节点的数据提前载入到内存,提高读取性能,在 Lucene8.4.0~Lucene 8.6.0这个版本区间,使用了off-heap机制载入图7中的PackedIndexValue字段的值,使得在搜索阶段才根据某个点数据域载入所属PackedIndexValue字段,并且内部节点跟叶子节点的数据都在同一个索引文件.dim中。
更好的检查索引文件的合法性,在优化之前只能通过检查Footer来判断,关于索引文件的合法性的检查在后面的文章会展开。

off-heap

 

对于off-heap在点数据域中的使用,可以分为三个阶段,另外下文中代码读取的索引文件为.dim或者.kdm:

 

不使用off-heap

 

在Lucene 8.4.0之前,不使用off-heap机制,即在生成DirectoryReader阶段,会把 所有段 中的 所有点数据域 的PackedIndexValue(图7中索引文件.dim的PackedIndexValue)读取到内存中。

 

图14:

 

 

图15:

 

 

在图14中,第86、87、89行代码分别读取了索引文件.dim中的pointCount、DocCount、Length字段,随后根据Length字段的值,往后读取Length个字节的数据,即PackedIndexValue字段的全量数据,并且最后写入到字节数组packedIndex。

 

off-heap参数化

 

在Lucene 8.4.0~Lucene 8.6.0的版本期间,参数化选择是否使用off-heap:

 

图16:

 

 

图16中,如果使用off-heap,那幺会把文件指针指向图14的PackedIndexValue字段的起始读取位置,随后在 搜索阶段 ,根据搜索条件中的点数据域,再读取出该域对应的PackedIndexValue的数据,否则在 生成DirectoryReader阶段 就将 所有段 中的 所有点数据域 的PackedIndexValue读取到内存中。

 

图16中第220行,使用offHeap取决于使用哪种 Directory ,例如使用MMapDirectory则会使用off-heap。

 

只使用off-heap

 

在Lucene 8.6.0的版本之后,直到目前最新版本的Lucene 8.6.3,总是使用off-heap:

 

图17:

 

 

图17中,代码第93行的if语句判断的是当前读取的点数据的索引文件的版本号,如果是Lucene 8.6.0以以上,那幺按照图8的索引文件.kdm读取,否则按照图14的索引文件.dim读取,这里说明Lucene 8.6.0兼容低版本的点数据域对应的索引文件。

 

图17中,代码第97、99行读取的是Lucene8.6.0之前的索引文件,可以看出把文件指针指向了PackedIndexValue字段,但并没有读取。

 

由于PackedIndexValue的数据不在索引文件.kdm中,所以只能使用off-heap。

 

另外版本号通过Header字段获取,其包含的其他内容不是很重要,故省略,如下图 红框 标注:

 

图18:

 

 

结语

 

 

点击下载 附件

Be First to Comment

发表评论

电子邮件地址不会被公开。 必填项已用*标注