本文承接文章索引文件的合并(一)之fdx&&fdt&&fdm,继续介绍剩余的内容,下面先给出索引文件fdx&&fdt&&fdm的合并流程图。
索引文件fdx&&fdt&&fdm的合并流程图
图1:
DocMap[ ]数组
继续介绍图1的流程点之前,我们先介绍下DocMap[ ]数组,如果合并后的段是段内排序的,那么需要在构造MergeState(见文章索引文件的合并(一)之fdx&&fdt&&fdm的介绍)对象期间生成DocMap[ ]数组,数组中的数组元素描述了某个待合并的段的一个映射关系,它描述了段中文档号到新段文档号的映射关系。
图2:
图2中描述了待合并的段中的文档号映射到新段后的文档号。其中待合并的第一个段中的文档号3以及待合并的第二个段的文档号2用红框额外标注,它们表示这两篇文档号是被标记为删除的,在合并后的新段中不会出现这两个文档号映射关系(代码中其实有删除文档号到新段文档号的映射,只是在合并的过程中会通过索引文件之liv过滤,所以相当于没有映射关系)。图2中的映射关系使用DocMap[ ]数组存储:
图3:
图2中的映射关系是如何生成的:
回答这个问题前必须再次强调在文章索引文件的合并(一)之fdx&&fdt&&fdm中的内容:待合并的段中的文档号是段内有序的,并且所有的段的排序规则都是相同的。
生成图2的映射关系的大概逻辑就是:
- 把所有的段丢进一个优先级队列中,所以这个队列中的元素数量为待合并的段的数量
- 优先级队列的排序规则为比较文档中的一个或者多个排序域(排序域即跟IndexWriter中的IndexSort相同域名的域),如果排序域仍然无法比较出结果,那么就根据文档所属段的编号(生成一个段的生成是同步的,先生成的段对应的编号小),编号越小则胜出。
- 每个段按照文档号大小的顺序,每次取出一篇文档中的排序域参与比较,如果胜出,那么这篇文档的文档号建立与新段中的文档号的映射,接着这个段取出下一篇文档中的排序域继续参与比较,随后优先级队列重排
详细的过程可以看源码,实现方式易读易懂:https://github.com/LuXugang/Lucene-7.5.0/blob/master/solr-8.4.0/lucene/core/src/java/org/apache/lucene/index/MultiSorter.java 。虽然是Lucene 8.4.0的链接,但是跟Lucene 8.7.0中的实现是一样的。
图4:
我们看下MultiSorter的注释,它提到所有的leaf reader必须是有序的并且使用相同的排序规则,即上文中提到的待合并的段中的文档号是段内有序的,并且所有的段的排序规则都是相同的。
初始化优先级队列
图5:
当前流程点需要初始化了一个优先级队列,目的在于随后的剩余的流程中我们将通过这个优先级队列有序的取出获得一个文档号,它对应的索引文件信息将被写入到新的索引文件。
初始化优先级队列的大概逻辑为:
- 把所有的段丢这个优先级队列中
- 优先级队列的排序规则为比较段中的文档号映射到新段的文档号,我们称之为mappedDocID(源码中的变量名),mappedDocID越小优先级越高。获得mappedDocID的方式正是上文中提到的DocMap[ ]数组
看这里:https://www.amazingkoala.com.cn/Lucene/Index/2020/1202/181.html