构造 IndexWriter 对象(八)

构造 IndexWriter 对象(七)
构造 IndexWriter 对象(六)
构造 IndexWriter 对象(五)
构造 IndexWriter 对象(四)
构造 IndexWriter 对象(三)
构造 IndexWriter 对象(二)
构造 IndexWriter 对象(一)
  本文承接构造 IndexWriter 对象(七),继续介绍调用 IndexWriter 的构造函数的流程。

调用 IndexWriter 的构造函数的流程图

图 1:

1.png

生成对象 IndexFileDeleter

  我们紧接上一篇文章,继续介绍剩余的流程点,下面先给出 IndexFileDeleter 的构造函数流程图:

IndexFileDeleter 的构造函数流程图

图 2:

2.png

执行检查点(checkPoint)工作

图 3:

3.png

  在介绍当前流程点之前,我们先通过下面的流程图来介绍 执行检查点的工作 这个流程点做了哪些事情。

  另外 执行检查点(checkPoint)工作 在源码中对应的方法方法定义:

public void checkpoint(SegmentInfos segmentInfos, boolean isCommit) throws IOException {
... ...
}
执行检查点(checkPoint)工作的流程图

图 4:

4.png

准备数据

图 5:

5.png

  图 4 流程图的准备数据就是 checkPoint 方法的两个参数 SegmentInfos 以及 boolean。

增加 SegmentInfos 对应的索引文件的计数引用

图 6:

6.png

  在后面的流程中,可能会执行索引文件的删除,如果某些索引文件被 SegmentInfos 引用,那么这些索引文件不应该被删除,防止被删除的方法就是增加 SegmentInfos 对应的索引文件的计数引用。

当 isCommit 为 true 时的处理流程

图 7:

7.png

  在执行 commit()操作时,也会执行 checkPoint 的操作,那么此时的 isCommit 是位 true 的,在文章文档提交之 commit(二)中介绍了这个流程,不赘述。

当 isCommit 为 false 时的处理流程

图 8:

8.png

  lastFiles 是一个 IndexFileDeleter 类的成员变量,它用来存放上次执行 checkPoint 的 SegmentInfos 中对应的索引文件定义如下:

private final List lastFiles = new ArrayList<>();  所以当 isCommit 为 false 时,先尝试删除 lastFiles 中的索引文件,删除的方式就是减少每一个索引文件的计数引用,如果计数值为 0,那么该索引文件就要被删除,否则不删除,最后清空 lastFiles 中的索引文件后,将这次 SegmentInfos 对应的索引文件添加到 lastFiles 中。



  结合图 6 跟图 8 我们可以发现这种流程的逻辑设计可以使得,即使对同一个 SegmentInfos 对象执行多次 checkPoint 的操作时,如果该对象中的段没有发生变化,那么段对应索引文件的计数是不会变的(先增加计数,再减少计数),同样地,如果 SegmentInfos 中如果增加了段,能通过图 6 的流程点对该段中的索引文件执行正确的 +1 计数,如果删除了某个段,能通过图 8 的流程点 尝试删除lastFiles中的索引文件 对该段中的索引文件执行正确的-1 计数。

为什么要通过 checkPoint 来实现索引文件的删除

  Lucene 通过 IndexWriter 对象中的成员变量 SegmentInfos 来描述当前 IndexWriter 对应的索引信息,索引信息的变化通过 SegmentInfos 对象来反应,但是 SegmentInfos 对象并不真正的拥有索引文件的数据,它拥有只是这些索引文件的文件名,所以当 SegmentInfos 对应的信息发生变化时,例如某个段中包含的所有文档满足某个删除操作,该段的信息会从 SegmentInfos 中移除(段的信息即 SegmentCommitInfo,见文章构造 IndexWriter 对象(四)关于流程点 获得回滚(rollback)信息 的介绍),那么这个段对应的索引文件也应该要删除(如果索引文件的计数引用为 0),当然如果其他段同时引用这些索引文件,那么就不会被删除(索引文件的计数引用不为 0),也就是为什么图 7 的流程点 尝试删除lastFiles中的索引文件 为什么要带有 尝试 两个字。

我们回到当前流程点,介绍下为什么获得 StandardDirectoryReader 后需要执行 执行检查点(checkPoint)工作

  根据图 2 的流程,我们是在构造 IndexFileDeleter 对象的流程中第一次调用 checkPoint()方法,那么 lastFiles 中必定不包含任何的数据,并且在源码中调用 checkPoint()方法的参数如下所示:

checkpoint(segmentInfos, false);  segmentInfos 即 StandardDirectoryReader 中对应的索引信息(见文章构造 IndexWriter 对象(四)用StandardDirectoryReader初始化一个新的SegmentInfos对象 流程点的介绍),同时 isCommit 的值为 false,也就说在当前流程点调用 checkPoint()方法的目的就是仅仅为了增加 segmentInfos 对应的索引文件的计数,那么就转变为如下的问题:

为什么获得 StandardDirectoryReader 后,需要增加 segmentInfos 对应的索引文件的计数

  首先给出源码中的解释:

// Incoming SegmentInfos may have NRT changes not yet visible in the latest commit, so we have to protect its files from deletion too:  我们用一个例子来介绍上文的注释所描述的场景,完整代码见:https://github.com/LuXugang/Lucene-7.5.0/blob/master/LuceneDemo/src/main/java/lucene/index/CheckPointInIndexFileDeleter.java

图 9:

9.png

  图 9 中当第 62 行的 oldIndexWriter.commit()操作执行结束后,索引目录中的索引文件如下所示:

图 10:

10.png

  接着执行完第 64 行的代码后,我们获得了一个 NRT 的 Reader(见文章近实时搜索 NRT(三)的介绍),接着第 70 行代码结束后,oldIndexWriter 新增了一篇文档 2,注意的是并没有执行 commit()操作(即没有生成新的 segments_N 文件),随后通过第 72 行的 openIfChange()方法获得一个包含最新索引信息的 reader(即 StandardDirectoryReader),通过该 reader 获得一个 IndexCommit,IndexCommit 中包含了第 70 行代码增加的索引信息,即图 11 中以_1 为前缀的索引文件,并且在第 76 行通过 IndexWriterConfig.setIndexommit()方法将 IndexCommit 成为 newIndexWriter 的配置,在执行完第 78 行代码后,索引目录中的索引文件如下所示:

图 11:

11.png

  从图 11 可以看出,以_1 为前缀的索引文件无法被最后的一次 commit()可见,即 Incoming SegmentInfos may have NRT changes not yet visible in the latest commit,那么在下面的用红色标注的流程中,就无法通过 segments_N 文件来增加_1.cfe、_1.cfs、_1.si 的计数,这些索引文件就是通过 NRT changes 获得的,在后面的流程中,我们将会知道计数为 0 的文件都会被删除:

图 12:

12.png

对 commits 集合进行排序

图 13:

13.png

  在文章构造 IndexWriter 对象(七)中,根据索引目录中的 segments_N 文件,将对应的 segments_N 对应的 SegmentInfos 生成 CommitPoint,并且添加到 CommitPoint 集合 commits 中,添加的过程是无序的,如果构造中的 IndexWriter 对象使用的是默认的索引删除策略 KeepOnlyLastCommitDeletionPolicy,那么就无法正确的处理 了,所以需要按照从早到晚的顺序来排序。

结语

  基于篇幅,剩余的内容将在下一篇文章中展开。

阅读原文https://www.amazingkoala.com.cn/Lucene/Index/2019/1203/113.html


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