Lucene知识总结

(1) Lucene查询上只能提供近实时而非实时查询,原因是Segment在被flush或commit之前,数据保存在内存中,是不可被搜索的。

(2) IndexWriter提供的核心接口都是线程安全的,并且内部做了特殊的并发优化来优化多线程写入的性能。IndexWriter内部为每个线程都会单独开辟一个空间来写入,这块空间由DocumentsWriterPerThread(简称DWPT)来控制。整个多线程数据处理流程为:

  1. 多线程并发调用IndexWriter的写接口,在IndexWriter内部具体请求会由DocumentsWriter来执行。DocumentsWriter内部在处理请求之前,会先根据当前执行操作的Thread来分配DocumentsWriterPerThread。

  2. 每个线程在其独立的DocumentsWriterPerThread空间内部进行数据处理,包括分词、相关性计算、索引构建等。

  3. 数据处理完毕后,在DocumentsWriter层面执行一些后续动作,例如触发FlushPolicy的判定等。

引入DWPT后,Lucene内部在处理数据时,整个处理步骤只需要对以上第一步和第三步进行加锁,第二步完全不用加锁,每个线程都在自己独立的空间内处理数据。而通常来说,第一步和第三步都是非常轻量级的,而第二步是对计算和内存资源消耗最大的。所以这样做之后,能够将加锁的时间大大缩短,提高并发的效率。每个DWPT内单独包含一个In-memory buffer,这个buffer最终会flush成不同的独立的segment文件。

(3) flush:flush是将DWPT内In-memory buffer里的数据持久化到文件的过程,flush会在每次新增文档后由FlushPolicy判定自动触发,也可以通过IndexWriter的flush接口手动触发。每个DWPT会flush成一个segment文件,flush完成后这个segment文件是不可被搜索的,只有在commit之后,所有commit之前flush的文件才可被搜索。

(4) commit:commit时会触发数据的一次强制flush,commit完成后再此之前flush的数据才可被搜索。commit动作会触发生成一个commit point,commit point是一个文件。Commit point会由IndexDeletionPolicy管理,lucene默认配置的策略只会保留last commit point,当然lucene提供其他多种不同的策略供选择。

(5) merge:merge是对segment文件合并的动作,合并的好处是能够提高查询的效率以及回收一些被删除的文档。Merge会在segment文件flush时触发MergePolicy来判定自动触发,也可通过IndexWriter进行一次force merge。

(6) close:close = commit + flush + merge

(7) 单线程内,相同的IndexWriter对象,一并commit或先后commit都没有问题。

(8) 单线程内,不同的IndexWriter对象,如果对象A还未close就操作对象B,结果抛出异常(LockObtainFailedException),如果对象A close后再操作对象B则没有问题。

(9) 多线程环境下,相同的IndexWriter对象,先后commit没有问题(线程安全)。

(10) 多线程环境下,不同的IndexWriter对象,道理同(7),close一个才能操作另外一个。

(11) 单线程内,IndexWriter操作完成后commit才能使用IndexReader,多线程环境下则没有问题。

(12) 在Web环境下,IndexReader(IndexSearcher)和IndexWriter都推荐使用单例模式(消耗较大,线程安全)。下面是一个标准例子。

 1 package XXX;
 2 
 3 import lombok.extern.slf4j.Slf4j;
 4 import org.apache.lucene.analysis.Analyzer;
 5 import org.apache.lucene.analysis.standard.StandardAnalyzer;
 6 import org.apache.lucene.index.DirectoryReader;
 7 import org.apache.lucene.index.IndexReader;
 8 import org.apache.lucene.index.IndexWriter;
 9 import org.apache.lucene.index.IndexWriterConfig;
10 import org.apache.lucene.search.IndexSearcher;
11 import org.apache.lucene.store.Directory;
12 import org.apache.lucene.store.FSDirectory;
13 import org.springframework.beans.factory.annotation.Value;
14 import org.springframework.stereotype.Service;
15 import org.springframework.transaction.annotation.Transactional;
16 
17 import java.io.IOException;
18 import java.nio.file.Paths;
19 
20 @Service
21 @Slf4j
22 public class LuceneService {
23 
24     @Value("${app.property.index-dir}")
25     private String indexDir;
26 
27     private Directory directory;
28 
29     private IndexReader indexReader;
30 
31     private IndexWriter indexWriter;
32 
33     /**
34      * Get Directory.
35      *
36      * @return Directory
37      */
38     private synchronized Directory getDirectory() {
39         if (directory == null) {
40             try {
41                 directory = FSDirectory.open(Paths.get(indexDir));
42             } catch (IOException e) {
43                 throw new RuntimeException("Create Directory failed!", e);
44             }
45         }
46         return directory;
47     }
48 
49     /**
50      * Get IndexReader/IndexSearcher.
51      *
52      * @return IndexReader
53      */
54     public synchronized IndexReader getIndexReader() {
55         try {
56             if (indexReader == null) {
57                 indexReader = DirectoryReader.open(getDirectory());
58             } else {
59                 IndexReader newReader = DirectoryReader.openIfChanged((DirectoryReader) indexReader);
60                 if (newReader != null) {
61                     // close the old indexReader
62                     indexReader.close();
63                     indexReader = newReader;
64                 }
65             }
66             return indexReader;
67         } catch (IOException e) {
68             throw new RuntimeException("Create IndexReader failed!", e);
69         }
70     }
71 
72     /**
73      * Get IndexSearcher.
74      * Recommend instead of IndexReader.
75      *
76      * @return IndexReader
77      */
78     public IndexSearcher getIndexSearcher() {
79         return new IndexSearcher(getIndexReader());
80     }
81 
82     /**
83      * Get IndexWriter.
84      *
85      * @return IndexWriter
86      */
87     public synchronized IndexWriter getIndexWriter() {
88         if (indexWriter == null) {
89             Analyzer analyzer = new StandardAnalyzer();
90             IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
91             try {
92                 indexWriter = new IndexWriter(getDirectory(), indexWriterConfig);
93             } catch (IOException e) {
94                 throw new RuntimeException("Create IndexWriter failed!", e);
95             }
96         }
97         return indexWriter;
98     }
99 }

 

posted @ 2021-06-21 18:13  Storm_L  阅读(348)  评论(0编辑  收藏  举报