构造对象三
系列文章:
构造一个 IndexWriter 对象的流程总体分为下面三个部分:
- 设置索引目录 Directory
- 设置 IndexWriter 的配置信息 IndexWriterConfig
- 调用 IndexWriter 的构造函数
大家可以查看文章 构造 IndexWriter 对象(一)、 构造 IndexWriter 对象(二) 来了解前两部分的内容,我们接着继续介绍最后一个部分,即调用 IndexWriter 的构造函数。
IndexWriter 类有且仅有一个有参构造函数,如下所示:
public IndexWriter(Directory d, IndexWriterConfig conf) throws IOException {
... ...
}
其中参数 d 以及 conf 正是分别由 设置索引目录Directory
、 设置IndexWriter的配置信息IndexWriterConfig
两部分获得。
调用 IndexWriter 的构造函数的流程图
图 1:
获取索引目录的索引文件锁
图 2:
该流程为 Lucene 使用索引文件锁对索引文件所在的目录进行加锁,使得同一时间总是只有一个 IndexWriter 对象可以更改索引文件,即保证单进程内(single in-process)多个不同 IndexWriter 对象互斥更改(多线程持有相同引用的 IndexWriter 对象视为一个 IndexWriter 不会受制于 LockFactory,而是受制于对象锁(synchronized(IndexWriter))、多进程内(multi-processes)多个对象互斥更改。
更多关于索引文件锁的介绍可以看文章 索引文件锁 LockFactory。
获取封装后的 Directory
图 3:
该流程中我们需要对 Directory 通过 LockValidatingDirectoryWrapper 对象进行再次封装, 使得在对索引目录中的文件进行任意形式的具有"破坏性"(destructive)的文件系统操作(filesystem operation)前尽可能(best-effort)确保索引文件锁是有效的(valid)。
索引目录中的"破坏性"的文件系统操作包含下面几个内容:
- deleteFile(String name)方法:删除索引目录中的文件
- createOutput(String name, IOContext context)方法:在索引目录中创建新的文件
- copyFrom(Directory from, String src, String dest, IOContext context)方法:在索引目录中,将一个文件中的内容 src 复制到同一个索引目录中的另外一个不存在的文件 dest
- rename(String source, String dest)方法:重命名索引目录中的文件
- syncMetaData()方法:磁盘同步操作
- sync(Collection names)方法:磁盘同步操作
获取 IndexCommit 对应的 StandardDirectoryReader
图 4:
如果 IndexWriter 的配置信息 IndexWriterConfig 设置了 IndexCommit 配置,那么我们需要获得描述 IndexCommit 中包含的信息的对象,即 StandardDirectoryReader,生成 StandardDirectoryReader 的目的在后面的流程中会展开介绍,这里只要知道它的生成时机即可。
IndexCommit 的介绍可以查看文章 构造 IndexWriter 对象(一),而 StandardDirectoryReader 的介绍可以查看 近实时搜索 NRT、 SegmentReader 系列文章,这里不赘述。
根据不同的 OpenMode 执行对应的工作
图 5:
从图 5 中可以看出,尽管 Lucene 提供了三种索引目录的打开模式,但实际上只有 CREATE 跟 APPEND 两种打开模式的逻辑,三种模式的介绍可以看文章 构造 IndexWriter 对象(一),这里不赘述。
在源码中,使用一个布尔值 indexExists 来描述图 5 中的流程点 索引目录中是否已经存在旧的索引?
,如果存在,那么 indexExists 的值为 true,反之为 false。indexExists 在后面的流程中会被用到。
下面我们分别介绍 执行CREATE模式下的工作
、 执行APPEND模式下的工作
这两个流程。
执行 CREATE 模式下的工作的流程图
图 6:
配置检查 1
图 7:
该流程会检查用户是否正确设置了 IndexCommit 跟 OpenMode 两个配置,由于代码比较简单,故直接给出:
if (config.getIndexCommit() != null) {
// 条件一 if (mode == OpenMode.CREATE) { throw new IllegalArgumentException("cannot use IndexWriterConfig.setIndexCommit() with OpenMode.CREATE"); // 条件二 } else { throw new IllegalArgumentException("cannot use IndexWriterConfig.setIndexCommit() when index has no commit"); }}
上面的代码描述的是在设置了配置 IndexCommit 之后对 OpenMode 进行配置检查,其中 config 指的是 IndexWriter 的配置信息 IndexWriterConfig 对象:
- 条件一:如果用户设置的 OpenMode 为 CREATE,由于该模式的含义是生成新的索引或覆盖旧的索引,而设置 IndexCommit 的目的是读取已经有的索引信息,故这两种是相互冲突的逻辑,Lucene 通过抛出异常的方法来告知用户不能这么配置
- 条件二:如果用户设置的 OpenMode 为 CREATE_OR_APPEND,由于通过图 5 中的流程点
索引目录中是否已经存在旧的索引?
判断出 indexExists 的值为 false,即索引目录中没有任何的提交,但用户又配置了 IndexCommit,这说明用户配置的 IndexCommit 跟 IndexWriter 类的有参构造函数中的参数 d 必须为同一个索引目录
初始化一个新的 SegmentInfos 对象
图 8:
该流程只是描述了生成 SegmentInfos 对象的时机点,没其他多余的内容。
SegmentInfos 是什么:
- SegmentInfos 对象是 索引文件 segments_N 以及 索引文件。si 在内存中的描述,可以看文章 近实时搜索 NRT(一) 中关于流程点
获得所有段的信息集合SegmentInfos
的介绍,这里不赘述
同步 SegmentInfos 的部分信息
图 9:
如果索引目录中已经存在旧的索引,那么 indexExists 的值为 true,那么我们先需要获得旧的索引中的最后一次提交 commit 中的 SegmentInfos 中的三个信息,即 version、counter、generation:
- version:该值用来描述 SegmentInfos 发生改变的次数,即索引信息发生改变的次数
- counter:它跟下划线“_”作为一个组合值,用来描述下一次
- 原文作者:知识铺
- 原文链接:https://index.zshipu.com/geek/post/%E4%BA%92%E8%81%94%E7%BD%91/%E6%9E%84%E9%80%A0%E5%AF%B9%E8%B1%A1%E4%B8%89/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。
- 免责声明:本页面内容均来源于站内编辑发布,部分信息来源互联网,并不意味着本站赞同其观点或者证实其内容的真实性,如涉及版权等问题,请立即联系客服进行更改或删除,保证您的合法权益。转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。也可以邮件至 sblig@126.com