本文承接文档提交之flush(七),继续依次介绍每一个流程点,本篇文章是介绍文档提交之flush流程的最后一篇文章。
图1:
图2:
在执行更新ReaderPool
流程之前,我们需要等待所有发布生成的段中的处理删除信息事件
执行结束。
为什么需要等待:
执行DWPT的doFlush()
。根据图1的流程图,即存在多线程执行流程点IndexWriter处理事件
,自动fulsh的线程可能会从eventQueue中取出发布生成的段中的处理删除信息事件
,那主动flush的线程只能等待所有的事件执行完成才能继续往下执行,否则会造成主动flush的不完整性,不完整性描述的是,调用主动flush的方法(IndexWriter.flush())已经完成,但删除信息可能还没有完成等待的逻辑是什么:
至少主动flush必须(must be)要处理的删除信息
都已经完成了 为什么是至少主动flush必须(must be)要处理的删除信息
都已经完成了,至少主动flush必须(must be)要处理的删除信息
这句话需要拆分为两部分来介绍:
主动flush的线程如何获得当前update状态:
xxxxxxxxxx
Set<FrozenBufferedUpdates> waitFor;
synchronized (this) {
waitFor = new HashSet<>(updates);
}
图3:
ReaderPool是什么:
xxxxxxxxxx
private final Map<SegmentCommitInfo,ReadersAndUpdates> readerMap = new HashMap<>();
故更新ReaderPool
的过程就是更新每一个SegmentCommitInfo对应的ReadersAndUpdates的过程。
图4:
从IndexWriter的全局变量segmentInfos中依次取出每一个SegmentCommitInfo:
xxxxxxxxxx
private final SegmentInfos segmentInfos;
图5:
在文档提交之flush(七)中我们了解到全局FrozenBufferedUpdates中的删除信息会作用到segmentInfos中的每一个SegmentCommitInfo中,同时段内FrozenBufferedUpdates中的删除信息会作用到本段的SegmentCommitInfo,即当作用(apply)了删除信息后,每一个段中的删除信息可能会发生变化。如果一个段中有新的被标记为删除的文档产生,那么被删除的文档会被记录到.liv索引文件中,如果该段已经存在.liv索引文件,那么先生成一个新的.liv索引文件,然后删除旧的.liv索引文件(通过索引文件计数引用来判断是否能删除该索引文件,见文档提交之flush(七))。
如果存在更改DocValues域的操作,那么需要更新DocValues域,这部分在后面介绍软删除的文章中会展开介绍。
图6:
在上面的流程中,通过.liv索引文件确定了每一个段中的被删除的文档集合,如果一个段中所有文档都被标记为删除的,那么需要丢弃该段。
在执行完更新ReaderPool
流程之后,Lucene还提供了一个钩子函数,用户可以根据具体业务来实现这个接口,这个接口同文档提交之flush(六)中的图3中执行flush后的工作
流程是同一个钩子函数。
每一次索引发生变化,都会尝试判断是否需要执行段的合并操作,其判断条件依据不同的合并策略而有所不同,合并策略的文章可以看这里:LogMergePolicy、TieredMergePolicy。
至此,除了跟DocValues相关的知识点,我们通过八篇文章详细的介绍了执行了IndexWriter.flush()的所有流程。
在后面的文章中,我们会介绍软删除的内容,填补未展开的坑。
点击下载附件