构造 IndexWriter 对象(九)

构造 IndexWriter 对象(八)

构造 IndexWriter 对象(七)

构造 IndexWriter 对象(六)

构造 IndexWriter 对象(五)

构造 IndexWriter 对象(四)

构造 IndexWriter 对象(三)

构造 IndexWriter 对象(二)

构造 IndexWriter 对象(一)

  本文承接 构造 IndexWriter 对象(九),继续介绍调用 IndexWriter 的构造函数的流程。

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

图 1:

生成对象 IndexFileDeleter

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

IndexFileDeleter 的构造函数流程图

图 2:

执行检查点(checkPoint)工作

  在上一篇文章中,我们简单提了一下该流程点,其中 checkPoint 的作用及其逻辑在文章 构造 IndexWriter 对象(八) 已经介绍,不赘述,我们关注问题是为什么在当前流程点还要执行 checkPoint 的工作,这也是上一篇文章遗留的问题。

为什么这里还要执行一次 checkPoint 的工作

  先给出源码中的注释:

Always protect the incoming segmentInfos since sometime it may not be the most recent commit  上述注释中的 segmentInfos 即图 2 流程图中的准备数据 SegmentInfos,该段注释展开后的具体内容描述的是如果最后一次 commit(索引目录 segments_N 中 N 值最大的那次提交)中不包含该 SegmentInfos 信息,那么为了防止 SegmentInfos 对应的索引信息因为某些索引删除策略 IndexDeletionPolicy 被删除,故需要执行 checkPoint 的工作。

  我们以一个例子来描述对应的场景,该例子中的 oldIndexWriter 使用的索引删除策略是 NoDeletionPolicy,完整 demo 见: https://github.com/LuXugang/Lucene-7.5.0/blob/master/LuceneDemo/src/main/java/lucene/index/PersistentSnapshotDeletionPolicyTest.java

图 3:

  图 3 中,在执行了第 56 行以及第 62 行的代码的 oldIndexWriter.commit()方法后,索引目录中生成了两个段,如下所示:

图 4:

  接着在执行了第 63 行的代码后,我们通过索引删除策略 PersistentSnapshotDeletionPolicy 对索引目录中最新的一次提交生成一个快照,该提交即 segments_2,并通过 PersistentSnapshotDeletionPolicy.snapshot() 方法获得一个 IndexCommit 对象,并且在第 75 行我们将这个 IndexCommit 对象作为构造 newIndexWriter 的配置,此时索引目录中的内容如下所示:

图 5:

  图 5 中 snapshots_0 即生成的快照。

  IndexCommit 对象中的索引信息即快照对应的索引信息,即 segments_2 对应的索引信息,对应的索引文件即_0.cfe、_0.cfs、_0.si、_1.cfe、_1.cfs、_1.si,如下图所示:

图 6:

  图 6 中,根据 SegmentCommitInfo 的 SegName 字段获得对应的 索引文件。si

  我们顺便给出 segments_1 包含的索引信息,在后面的流程中会用到:

图 7:

  接着在执行了第 70 行的删除文档操作后,由于文档 0 跟文档 1 都满足该删除条件,即文档 0 跟文档 1 中都包含域名为"author",域值为"Lucy"的信息,那么在执行了第 71 行的 oldIndexWriter.commit()后,生成的第三个段中就不会包含文档 1 以及文档 2 的信息,即不会包含以_0 为前缀和以_1 为前缀的段的信息,索引目录中的内容如下所示:

图 8:

  segments_3 包含的索引信息如下所示:

图 9:

  图 9 中以_2 为前缀的段的索引信息即在图 3 中文档 2 对应的内容。

  接着执行图 3 中第 77 行代码构造 newIndexWriter,执行完下图中标注的流程后,会根据索引目录中的 segments_N 文件对其对应的索引文件执行计数 +1 的操作:

图 10:

  根据图 8 的索引目录中的内容,有 segments_1、segments_2、segments_3 共三个 segments_N 文件,他们对应的索引文件的计数如下所示:

表 1:

段名/索引文件_0.cfs_0.cfe_0.si_1.cfs_1.cfe_1.si_2.cfs_2.cfe_2.sisegments_1111000000segments_2111111000segments_3000000111计数和值222111111

  根据图 6、图 7、图 9,每个 segments_N 文件对应的索引文件计数和如上所示,它描述了这些索引文件被引用的次数,故称为计数引用。

  在继续介绍之前,我们先介绍下图 10 中标注的流程点 执行CommitPoint的删除工作

  • 根据索引删除策略,那些应该被删除的提交在标注的流程点 执行索引删除策略 执行结束后,这些提交只是被添加到 待删除队列 中,并没有正在被删除,真正的删除工作是在流程点 执行CommitPoint的删除工作 完成的

如何执行删除工作

  其过程就是对所有待删除的提交对应的索引文件执行计数-1 的操作,如果计数值为 0,那么索引文件就会被删除。

  我们回到图 3 的例子,当执行到图 10 中的标注的 流程时,如果图 3 中的 newIndexWriter 使用的是 KeepOnlyLastCommitDeletionPolicy,该索引删除策略描述的是只保留最新的提交,即只保留 segments_3 对应的索引信息,segments_1 跟 segments_2 对应的索引信息都需要被删除,故这两个段在流程点 执行索引删除策略 中被添加到 待删除 队列, 如果不执行 执行检查点(checkPoint)工作,而是直接执行标注的流程点,那么根据表 1 中的内容,索引文件_1.cfs、_1.cfe、_1.si 由于在执行计数-1 的操作后,计数值都变为 0 而被删除,但由于构造当前 newIndexWriter 对象使用了 IndexCommit(快照 snapshot 的索引信息)配置,该对象对应的索引信息是 segments_2,而 segments_2 中包含索引文件_1.cfs、_1.cfe、_1.si 的索引信息,如果被删除,那么索引信息就会被破坏,所以我们必须在 执行CommitPoint的删除工作 之前先执行 执行检查点(checkPoint) 来增加 segments_2 对应的索引文件的计数值,而这就是注释所谓的"Always protect the incoming segmentInfos since sometime it may not be the most recent commit"。

  在执行完 执行检查点(checkPoint)工作 工作后,索引文�