文档提交之flush(七)

  本文承接文档提交之flush(六),继续依次介绍每一个流程点。

 

文档提交之flush的整体流程图

图1:

预备知识

Set<FrozenBufferedUpdates> update

  update中存放的是FrozenBufferedUpdates的集合,在文档提交之flush(六)中FrozenBufferedUpdates获得nextGen的值之后,它会被添加到update容器中,在FrozenBufferedUpdates中的删除信息作用到段之后,从update中移除。

IndexWriter处理事件

  文档提交之flush(六)中我们介绍了IndexWriter处理事件流程中的几种事件类型,下面仅介绍发布生成的段中的处理删除信息事件

处理删除信息

  根据作用(apply)的目标段,处理删除信息划分为两种处理方式:

处理删除信息的流程图

图2:

点击查看大图

  该流程在以下三种情况下会被调用:

  上述的三种情况会发生并发执行同一个FrozenBufferedUpdates的作用(apply)删除信息工作。

是否已经处理过当前删除信息

图3:

  如上文中介绍,多线程可能同时执行同一个FrozenBufferedUpdates的作用(apply)删除信息的工作,即图2的流程,但只允许一个线程执行,否则会重复的处理删除信息,其他线程会被阻塞直到获得锁的线程执行完图2的流程。

  通过什么方式判断其他线程已经处理过当前删除信息

获得当前已经完成段合并的计数mergeGenStart

图4:

  执行段合并的线程在执行完一次段合并之后,会递增一个计数,即mergeGenStart,该值会在后面的流程介绍,这里只要知道获得mergeGenStart所在的流程点位置。

获得被作用的段集合infos

图5:

  文档提交之flush(六)中我们了解到,描述新生成的段以及旧段的索引信息SegmentCommitInfo都存放在IndexWriter的全局变量SegmentInfos中,此流程从SegmentInfos找到那些将要被作用删除信息的段的集合,根据当前处理的FrozenBufferedUpdates是全局还是段内,获取的方式有点区别:

  infos为空的后执行的流程在下文介绍。

  为什么这里需要通过IndexWriter对象实现同步

获取infos中所有SegmentCommitInfo的信息

图6:

  获取的信息被保存到SegmentState[ ]数组中,在介绍数组元素包含的信息前,得先说明只有满足下列要求的SegmentCommitInfo才会将其信息保存到SegmentState[ ]数组中:

  首先保证bufferedDeletesGen <= delGen,上文中我们提到的,根据全局FrozenBufferedUpdates内的nextGen(见文档提交之flush(六))值,即delGen,其删除信息将要作用到所有比该nextGen(delGen)值小的段,其中等号"="考虑是段内的FrozenBufferedUpdates的delGen跟此段的delGen是相等的情况。

  再满足了上面的条件之后,就可以获取infos中所有满足条件的SegmentCommitInfo的信息了。SegmentState[ ]数组中的数组元素至少(跟本篇文章相关的)包括了下面的信息:

  为什么要增加段集合中的所有索引文件计数引用

处理删除信息

图7:

  图7中,处理TermDeletes即处理删除信息TermArrayNode、TermNode;处理QueryDeletes即处理删除信息QueryArrayNode;处理DocValuesUpdates即处理删除信息DocValuesUpdatesNode。其删除信息的介绍看文档的增删改(下)(part 2)

  上文中我们获得了SegmentReader,该值使得我们能获得段中的索引信息,包括文档的信息:

  尽管我们没有对上述几个处理逻辑进行展开介绍,但这三个流程最终的目的就是找出满足删除要求的文档号,通过ReaderPool对象暂存TermDeletes、QueryDeletes生成的删除信息以及DocValuesUpdates生成的更新信息。最后在图1的流程点更新ReaderPool中将删除信息以及更新信息生成索引文件。

  如果图2中处理的是段内FrozenBufferedUpdates,那么是不用处理处理TermDeletes的,因为删除信息TermArrayNode、TermNode在生成索引文件.tim、.tip.doc.pos、.pay阶段就被处理了(见文档提交之flush(三))。

处理完删除信息后的工作

图8:

  在处理完删除信息后,我们需要以下的工作:

再次处理DocValuesUpdates

图9:

  该流程会在后面介绍软删除的文章中展开介绍,这里只要知道其流程所在位置即可。

是否为段内删除信息?

图10:

  图2如果到达此流程点,并且段内FrozenBufferedUpdates的流程,那么我们已经成功的处理了段内的删除信息,故可以直接进入其下一个流程,该流程点在下文会介绍。

没有并发的段合并操作

图11:

  在上文的获得当前已经完成段合并的计数mergeGenStart中,我们获得了mergeGenStart,并且在当前流程点再次去获得段合并的计数mergeGenCur,如果mergeGenCur 与 mergeGenStart相等,说明图2的流程从开始到现在这段期间,没有其他线程执行段合并 或者 某些段合并操作还未结束,否则我们需要重新执行图2的流程,原因是索引目录中的段已经发生了变化,我们需要重新将全局的FrozenBufferedUpdates作用到索引目录中的段。

  从上面的流程可以看出,这种处理逻辑即乐观锁的一种实现方式。

  由于执行段合并的段集合只是索引目录中的部分段,所以有些段并没有发生变化,并且这些段已经被作用(apply)了删除信息,故可以存放到上文中提到的alreadySeenSegments中,使得在下一轮的图2流程中,不会重复作用删除信息。

处理FrozenBufferedUpdates

图12:

  处理FrozenBufferedUpdates的流程点逻辑跟图11中的是一致的,在退出图2流程之前,我们总是需要处理FrozenBufferedUpdates。只有mergeGenCur 与 mergeGenStart相等后才属于正确的处理删除信息,在其他流程点进入该流程点都属于未能正确处理删除信息。

  处理FrozenBufferedUpdates的工作如下,仅列出跟本篇文章有关的工作:

结语

  基于篇幅,图1中剩余流程点留到下一篇文章介绍。

点击下载附件