构造对象六
系列文章:
本文承接 构造 IndexWriter 对象(五),继续介绍调用 IndexWriter 的构造函数的流程。
调用 IndexWriter 的构造函数的流程图
图 1:
生成对象 BufferedUpdatesStream
图 2:
介绍该对象会涉及很多在 文档提交之 flush 系列文章中的知识点,故如果没有看过或者不熟悉 flush 流程的同学可以跳过下面的内容,只需要知道该对象的生成时机就行了。
BufferedUpdatesStream 用来追踪(track)FrozenBufferedUpdates,主要负责执行 FrozenBufferedUpdates 的两个工作:
- 获得 nextGen:它用来描述 FrozenBufferedUpdates 中的删除信息应该作用哪些段,见 文档提交之 flush(六) 文章中的介绍
- 作用(apply)删除信息:FrozenBufferedUpdates 中存放了删除信息以及更新信息(DocValues 相关),为了方便描述,在下文中 删除信息、更新信息统称为删除信息。删除信息被作用到每一个段称为处理删除信息,根据作用(apply)的目标段,处理删除信息划分为两种处理方式:
- 全局 FrozenBufferedUpdates:根据全局 FrozenBufferedUpdates 内的 nextGen(见 文档提交之 flush(六))值,其删除信息将要作用到所有比该 nextGen 值小的段
- 段内 FrozenBufferedUpdates:在 文档提交之 flush(三) 中我们提到,在生成索引文件的过程中,我们只处理了部分满足删除信息,即只处理了满足删除信息 TermArrayNode、TermNode(见 文档的增删改(下)(part 2))的段内部分文档,而如果段内 FrozenBufferedUpdates 还存在删除信息 QueryArrayNode、DocValuesUpdatesNode,那么根据段内 FrozenBufferedUpdates 就可以找出所有剩余的满足删除的文档
获得 nextGen 的执行时机点在 flush 的流程中的位置如下所示,用红框标注:
图 3:
图 3 的流程图的每个流程点的详细介绍见 文档提交之 flush(六)。
作用(apply)删除信息 的执行时机点在 flush 的流程中的位置如下所示,用红框标注:
图 4:
从图 3 的流程中可以知道,在 FrozenBufferedUpdates 获得 nextGen 之后就被添加到了 eventQueue(见 文档提交之 flush(四) 中的介绍)中,故该 作用(apply)删除信息 的执行时机点在图 4 的 IndexWriter处理事件
的流程中。
生成对象 DocumentsWriter
图 5:
DocumentsWriter 对象主要负责下面的三个工作:
- 文档的增删改:用户通过 IndexWriter 对象执行文档的增删改的任务,实际都是 IndexWriter 通过调用 DocumentsWriter 对象来实现的,文档的增删改的详细过程可以看 文档的增删改 的系列文章
- 将 DWPT 生成(flush)为一个段:该工作即图 4 中的流程
执行DWPT的doFlush()
- 执行主动 flush 以后的收尾工作:该内容见 文档提交之 flush(六) 中关于 DocumentsWriterFlushControl.finishFullFlush( ) 的方法的介绍
生成对象 ReaderPool
跟 BufferedUpdatesStream 一样,由于个人表达能力有限,无法通过有限的语句来描述 ReaderPool,故阅读下面的内容需要很多前置的内容,这些内容会以链接的方式给出,不会作详细的介绍,见谅。
ReaderPool 的命名方式就能完美描述该对象的作用,字面意思就是 存放 reader 的池子(pool),在源码注释中只用了一句话来描述该对象的作用,如下所示:
Holds shared SegmentReader instances ReaderPool 就是用来缓存 SegmentReader 对象(SegmentReader 用来描述一个段的索引信息,详细介绍可以看 SegmentReader 系列文章),使得 Lucene 在执行下面的操作时都会尝试先去 ReaderPool 取出 SegmentReader:
- 作用(apply)删除信息、更新 DocValues 信息
- 执行段的合并
- 分发(handing out)一个实时的 Reader
作用(apply)删除信息、更新 DocValues 信息
对于索引目录中的某一个段,由于后续有新的删除/更新操作,如果该段中的文档满足删除/更新的条件,那么该段对应的 SegmentReader 中的索引信息也需要发生更改,那么根据索引信息是否会被更改可以分为下面两类:
- 不会发生变更的索引信息:该索引信息即我们在文章 SegmentReader(一) 中介绍的 SegmentCoreReaders
- 会发生变更的索引信息:该索引信息即描述删除信息的 索引文件。liv、描述域信息的 索引文件。fnm、以及描述 DocValues 的 索引文件。dvd&&.dvm
生成一个 SegmentReader 对象的开销是极大的,原因是读取索引信息为磁盘 I/O 操作,故使用 ReaderPool 来缓存 SegmentReader,当需要作用(apply)删除信息、更新 DocValues 信息时,只需要从 ReaderPool 中取出该段对应的 SegmentReader(如果不存在则先添加到 ReaderPool),并且只修改 SegmentReader 中会发生变更的索引信息。
在 flush()阶段,DWPT(见文章 文档的增删改(中))被 flush 为一个段后,并不会马上被添加到 ReaderPool 中(lazy init 机制),而是当该段需要被作用(apply)删除信息、更新 DocValues 信息时,被添加到 ReaderPool 的时机点在下图中用红框标注:
图 6:
图 6 的流程图在文章 文档提交之 flush(七) 中做了详细介绍,感兴趣的同学可以看一看。
执行合并
执行段的合并的过程是通过每个段对应的 SegmentReader 中包含的索引信息进行合并(见 执行段的合并(三)),故在合并期间需要获取待合并段的 SegmentReader,而获取的方式就是从 ReaderPool 获取。
当然也有可能一个或多个待合并的段对应的 SegmentReader 并不在 ReaderPool(原因是没有 作用(apply)删除信息、更新 DocValues 信息),那么此时就需要生成新的 SegmentReader 对象,并添加到 ReaderPool 中。
分发(handing out)一个实时(real time)的 Reader
在文章 近实时搜索 NRT(一) 中我们说到,有下面的四种方法可以获得 StandardDirectoryReader:
- 方法一:DirectoryReader.open(final Directory directory)
- 方法二:DirectoryReader.open(final IndexCommit indexCommit)
- 方法三:DirectoryReader.open(final IndexWriter indexWriter)
- 方法四:DirectoryReader.open(final IndexWriter indexWriter, boolean applyAllDeletes, boolean writeAllDeletes)
其中通过方法三,方法四能获得具有 NRT 功能的 StandardDirectoryReader(见文章 近实时搜索 NRT(三)),并且在这两个方法的实现过程中,会将 StandardDirectoryReader 中的 SegmentReader 缓存到 ReaderPool 中,这样的做法使得当再次通过方法三、方法四或者性能更高的 OpenIfChange()方法( 近实时搜索 NRT(四))获得 StandardDirectoryReader 时,能先从 ReaderPool 中获得缓存的 SegmentReader,即所谓的"分发"。
实时(real time)的 StandardDirectoryReader 缓存到 ReaderPool 的时机点如下红框标注所示所示:
图 7:
图 7 的流程图的详细介绍见文章 近实时搜索 NRT(二)。
ReaderPool 的构造函数
另外还要说下的是,在 ReaderPool 的构造函数中,会将图 1 中流程点 获取IndexCommit对应的StandardDirectoryReader
获得的 StandardDirectoryReader 中的 SegmentReader 缓存到 ReaderPool 中。
生成对象 IndexFileDeleter
图 8:
IndexFileDeleter 用来追踪 SegmentInfos 是否还"活着(live)",在文章 构造 IndexWriter 对象(四) 中我们介绍了 SegmentInfos 对象跟索引文件 segments_N 的关系,简单的概括就是 SegmentInfos 对象是索引文件 segments_N 和索引文件。si 在内存中的表示。
当执行索引删除策略时,例如默认的索引删除策略 KeepOnlyLastCommitDeletionPolicy,新的提交生成后(即生成新的 segments_N 文件)需要删除上一次提交,即需要删除上一次提交对应的所有索引信息,而用来描述所有索引信息的正是 SegmentInfos,删除 SegmentInfos 的真正目的是为了删除对应在索引目录中的索引文件,但这些索引文件如果正在被其他 SegmentInfos 引用,那么就不�
- 原文作者:知识铺
- 原文链接:https://geek.zshipu.com/post/%E4%BA%92%E8%81%94%E7%BD%91/%E6%9E%84%E9%80%A0%E5%AF%B9%E8%B1%A1%E5%85%AD/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。
- 免责声明:本页面内容均来源于站内编辑发布,部分信息来源互联网,并不意味着本站赞同其观点或者证实其内容的真实性,如涉及版权等问题,请立即联系客服进行更改或删除,保证您的合法权益。转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。也可以邮件至 sblig@126.com