文档的增删改(中)

  文档的增删改(上)中,我们介绍了应用示例并给出了流程图,本篇文章承接上文,就流程图的每个流程点展开介绍,如果篇幅过长又得拖到下一篇文档啦😅。

预备知识

  在介绍流程点前需要了解以下几个知识点,下文使用到的名称都是源码中的类名。

DocumentsWriterPerThread

  该类实现了将文档转化为索引文件的所有需要的功能,如果你之前看过两阶段生成索引文件之第一阶段,这篇文章中描述了一篇文档转化为索引文件的过程的第一个阶段,而这个过程的入口即DocumentsWriterPerThread提供的processDocument( )方法。

  DocumentsWriterPerThread在处理完文档后,会收集到以下的数据,注意的这只是部分数据,并且是跟本篇文章相关的,而其他数据以及DocumentsWriterPerThread提供的其他方法(功能)则会在后面介绍flush的文章中进一步展开:

  DocumentsWriterPerThread在下文中简称DWPT,源码中也是这么干的😁。

DocumentsWriterPerThreadPool、ThreadState

  DocumentsWriterPerThreadPool是一个逻辑上的线程池,它实现了类似Java线程池的功能,在Java的线程池中,新来的一个任务可以从ExecutorService中获得一个线程去处理该任务,而在DocumentsWriterPerThreadPool中,每当IndexWriter要添加文档,会从DocumentsWriterPerThreadPool中获得一个ThreadState去执行,故在多线程(持有相同的IndexWriter对象引用)执行添加文档操作时,每个线程都会获得一个ThreadState对象。

  每一个ThreadState对象中都持有一个DWPT的引用,所以正如上文中所述,实际的添加文档操作还是DWPT。

  当ThreadState执行完添加文档的任务后,它会回到DocumentsWriterPerThreadPool中,等待下次的文档添加操作,通过一个名为freeList的链表来存储。

  ThreadState在两种情况下不持有一个DWPT的引用:

  用户可以通过LiveIndexWriterConfig对象自定义配置ramBufferSizeMB跟maxBufferedDocs的值,这两个变量的概念在后面介绍flush的文章中会详细展开。

获取ThreadState的流程图

图1:

点击查看大图

从DocumentsWriterPerThreadPool(DWPTP)中获取一个ThreadState

图2:

  当IndexWriter执行添加文档操作,比如说IndexWriter.addDocument()的操作,那么进入开始的流程点,由于可以多线程(持有相同的IndexWriter对象引用)添加文档,故使用synchronized(IndexWriter对象)关键字从DWPTP中获取ThreadState。

  如果DWPTP中没有ThreadState对象,那么直接生成一个新的ThreadState,如果存在,那么从freeList中的链表尾部取出一个ThreadState,因为当一个ThreadState完成添加文档的任务后,会重新回到DWPTP,即添加到freeList中,所以从链表尾部获取一个ThreadState即取出最近完成添加文档任务的ThreadState,即LIFO(Last In First Out)。

优先取出持有DWPT对象引用的ThreadState

图3:

  从freeList的链表尾部获得一个ThreadState对象后,我们这里称之为threadState1,如果threadState1中不持有DWPT对象的引用,那么需要从freeList的首部开始遍历每一个ThreadState,找到第一个持有DWPT对象引用的ThreadState对象,并且将threadState1重新添加到freeList中,当然也有可能链表中所有的ThreadState都不持有DWPT对象的引用。

  为什么要优先取出持有DWPT对象引用的ThreadState:

  为什么可能会取到不持有DWPT对象引用的ThreadState:

全局flush被触发

图4:

  当前的ThreadState持有DWPT对象引用,说明在上一步的流程中,从freeList中取到了一个刚刚完成文档添加任务的ThreadState,如果此时全局flush被触发,那么ThreadState会失去该DWPT的引用,而该DWPT被加入到flush队列,其包含的索引信息等待被写入到磁盘,并且ThreadState被重置(恢复到刚生成ThreadState对象的状态)

  这里简单提下常见的几个会触发全局flush的场景,其他场景以及具体的逻辑会在后面介绍flush的文章时展开:

让ThreadState持有一个DWPT对象的引用

图5:

  从上面的流程可以看出,到了这个流程点,从freeList中获取的ThreadState不一定持有DWPT对象的引用,故需要提供一个新的DWPT对象。当然新生成的hreadState在这个流程点也需要持有一个新的DWPT对象。

结束

图6:

  至此,获取ThreadState的流程已经结束,并且ThreadState必定是持有DWPT对象的引用。

FlushPolicy

  FlushPolicy即flush策略,准确的说应该称为 自动flush策略,因为flush分为自动flush跟主动flush,即显示的调用IndexWriter.flush( )方法,flushPolicy描述了IndexWriter执行了增删改的操作后,将修改后的索引信息写入磁盘的时机。

  Lucene7.5.0版本中,有且仅有一个flushPolicy:FlushByRamOrCountsPolicy

FlushByRamOrCountsPolicy

  FlushByRamOrCountsPolicy还定义了IndexWriter执行完增删改的操作后的后续工作:

DocumentsWriterFlushControl

  DocumentsWriterFlushControl类中定义了ThreadState在添加/更新文档过程中的各种行为,上文中提及的例如ThreadState失去持有DWPT的行为、activeBytes、flush队列等等都是在该类中定义,下面列出几个跟本篇文章内容相关的DocumentsWriterFlushControl类中的方法、变量:

结语

  要完全的深入了解文档增删改的过程,这些预备知识必须都先了解,果不其然,篇幅又超了,所以文档增删改的流程图的介绍又得拖到下一篇文章啦。

点击下载附件