27

IndexedDISI(二)-html

 3 years ago
source link: https://www.amazingkoala.com.cn/Lucene/gongjulei/2020/0514/141.html?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

IndexedDISI(二)(Lucene 8.4.0)

  在文章IndexedDISI(一)(阅读本文中之前,需要该前置文章)中我们介绍了在Lucene7.5.0中IndexedDISI的实现原理, 本文基于Lucene 8.4.0,将介绍优化后的IndexedDISI,即使用查找表(lookup table)提高了查询性能。

  我们先根据源码中的注释看下优化的目的与方式,也可以直接查看IndexedDISI.java文件中的Javadoc:



xxxxxxxxxx
To avoid O(n) lookup time complexity, with n being the number of documents, two lookup tables are used: A lookup table for block offset and index, and a rank structure for DENSE block index lookups.

  上述大意是,查找的时间复杂度为O(n),其中n是文档号的数量,优化方式为通过两个查找表来提高查询性能:

  • 第一个查找表使用offset跟index实现block之间的跳转,在源码中,使用int类型数组来存储offset跟index的信息,该数组的变量名为jumps
  • 第二个查找表使用rank结构(structure)实现在block的稠密度为DENSE中的word之间的跳转,数组的变量名为rank
1.png

第一个查找表jumps

  我们先看实现block之间跳转的查找表jumps数组,在jumps数组中,index跟offset作为一对(pair)来描述一个跳转信息:

  • offset:该值描述了block在字节流的起始读取位置,例如在生成索引文件.dvd&&.dim中使用IndexedDISI存储文档号时,offset就描述了block在索引文件.dvd中的起始读取位置
  • index:block中第一个文档号的段内编号(见文章IndexedDISI(一)

生成jumps

  我们通过一个例子来理解第一个查找表,如果我们以下的文档号集合:

2.png

  在文章IndexedDISI(一)我们说到,每个block用来描述最多2^16个文档号信息,例如第一个block中描述的文档号取值范围为[0, 2^16 - 1],第二个block描述的文档号取值范围为[2^16, 2^17 - 1],第三个block描述的文档号取值范围为[2^17, 2^18 - 1],故图1中的文档号集合将会由3个block来存储如下所示:

3.png

 对于图3的情况,生成的jumps数组如下所示:

4.png

  从图4可以看出,index描述的的确是block中第一个文档号的段内编号,例如在第三个block中,index的值为5003,意味着这个block中存储的第一个文档号的段内编是5003,至于为什么要存储index,我们将在系列文章索引文件的生成(十五)之dvm&&dvd中作出解释。

读取jumps

  同样通过一个例子来介绍如果通过jumps实现block之间的跳转。如果我们有一个待查询的文档号为131082,由于每个block中描述的文档号的最多为2^16个,那么通过下面的公式获得结果,我们称之为inRangeBlockIndex(源码中的变量名),可以计算出文档号131082应该由哪个block来描述,在代码中:



xxxxxxxxxx
130182 >>> 16 = 2

  由于jumps[ ]数组被存储在字节流中,所以继续根据根据下面的公式就可以获得inRangeBlockIndex为2对应的index跟offset的值:



xxxxxxxxxx
final int index = jumpTable.readInt(inRangeBlockIndex*Integer.BYTES*2);
final int offset = jumpTable.readInt(inRangeBlockIndex*Integer.BYTES*2+Integer.BYTES);

  上述公式中Integer.BYTES的值为4,jumpTable为存储jumps[ ]数组的字节流,故我们以jumps[ ]数组的第一个数组元素作为起始位置,偏移值为inRangeBlockIndex*Integer.BYTES*2 + Integer.BYTES的位置就可以读取offset,即文档号131082用第二个block来描述,至于在不在这个block中,需要进一步进行判断,下文中会展开介绍:

5.png

  在文章开头我们就说道,jumps数组是int类型,故4个数组元素占用16个字节。

第二个查找表rank

  在一个block内部,我们根据block的稠密度使用不同的数据类型存储,只有稠密度为DENSE时,会使用第二个查找表rank数组来实现word之间的跳转:

6.png

生成rank

  生成rank数组前,需要指定denseRankPower,该值描述的是block中每n个文档号就生成一个rank,对应关系如下:

denseRankPower文档数量
7128
8256
9512
101024
112048
124096
138192
1416384
1532768

  表一中,如果denseRankPower为7,那么block中每128个文档号就生成一个rank,由于一个word能最多表示64个文档号,意味着每2个word就生成一个rank,即图6中描述的那样。

  在图6中,每处理两个word,就将这两个word中包含的文档数量,即bitCount写入到rank数组中,注意的是bitCount是一个累加值,在介绍完下面的例子后会发现,rank数组中的bitCount用来表示的是一个rank中第一个文档号的段内编号,例子中denseRankPower为7:

7.png

  假设图6中的第一个block中存储了上述的文档号,其中图7中文档号的数量说明这个block的稠密度为DENSE(见文章p;在文章IndexedDISI(一)),那么上述文档号在使用word存储后如下所示:

8.png

  由于denseRankPower为7,意味着每两个word就要生成一个rank,并且两个word的bitCount和值记录到rank数组中:

9.png

  图9中,每个word的bitCount描述的是它存储的文档号数量,例如第一个word中存储了文档号1、56、61共三个文档号,故bitCount的值为3,由于两个word生成一个rank,所以两个word的bitCount的和值写入到rank数组的下标值为0的位置,即红框圈出的两个bitCount的和值,另外在继续处理了第三个、第四个共两个word后,我们此时需要生成第二个rank,不仅仅要获得这,两个word中的bitCount和值,还要累加上第一个跟第二个word的和值,即3 + 2 + 3 + 2 = 10,写入到rank数组的下标值为1的位置。

  图6中的HEB、LEB是 高八位 high eight bit、 lower eight bit的缩写,由于一个block中最多有65536个文档号,意味着bitCount的值最大值为65535,需要16个bit字节才能表示,加上rank数组是字节数组,所以需要两个数组元素即2个字节才能表示bitCount的最大值,故第一个字节用来表示高8位,第二个字节用来表示低8位。

读取rank

  在读取阶段,使用rank数组进行跳转的条件如下所示:



xxxxxxxxxx
denseRankPower != -1 && targetWordIndex - wordIndex >= (1 << (denseRankPower-6) )

  上述条件中,wordIndex是上一个文档号的段内编号,targetWordIndex是当前查询文档号的段内编号,可见只有前后两次查询的段内编号超过denseRankPower对应的文档数量才会进行跳转,理由很简单,读取阶段只会读取出denseRankPower对应的文档号信息到内存,如果满足上述条件,我们需要从磁盘(缓冲)中读取新的words,具体的读取过程由于跟读取jumps数组类似就不赘述了。

   在Lucene8.0.0之后,通过两个查找表使得在时间复杂度为O(n)的基础上提高了查询性能,对于表一中denseRankPower,源码中作者建议的取值范围为 [8, 12],更多关于two lookup table的设计思想见这个issue:https://issues.apache.org/jira/browse/LUCENE-8585?jql=text%20~%20%22indexedDISI%22

点击下载附件


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK