Lucene的索引文件,会包含很多个segments文件,每个segment中包含多个documents文件,一个segment中会有完整的正向索引和反向索引。
在搜索时,Lucene会遍历这些segments,以segments为基本单位独立搜索每个segments文件,而后再把搜索结果合并。
建立索引文件的过程,实际就是把documents文件一个个加入索引中,Lucene的做法是最开始为每个新加入的document独立生成一个segment,放在内存中。而后,当内存中segments数量到达一个阙值时,合并这些segments,新生成一个segment加入文件系统的segments列表中。
而当文件系统的segments数量过多时,势必影响搜索效率,因此需要不断合并这些segments文件。
那么Lucene的合并策略是什么?如何保证合适的segments数量呢?
其实Lucene有两套基本的策略:
第一种策略实现代码位于IndexWriter#optimize()函数,其实就是把所有segments文件合并成一个文件。合并的算法是递归合并列表最后的mergeFactor个segments文件直到合并成一个文件。这儿mergeFactor是Lucene的一个参数。
btw: 从算法细节上看,其实我不是喜欢这段实现,因为列表的最后mergeFactor个文件内容实际被扫描了segmens_count/mergeFactor次。如果分段归纳合并的方式不知道是否更好,每个segment文件内容都将被扫描 ceil(Log_mergeFactor(segmens_count)) 或ceil(Log_mergeFactor(segmens_count)) +1次,是否更好?
第二种策略实现代码位于IndexWriter#maybeMergeSegments()函数中,这个代码就复杂了,它的基本策略就是要求确保合并后两个公式的成立:
假定segments是个有序列表,B表示maxBufferedDocs,f(n)=ceil(log_M(ceil(n/B))),M表示mergeFactor,这儿maxBufferedDocs和mergeFactor是两个参数
1. 如果第i个segment的documents数量为x,第i+1个segment的documents数量为y,那么f(x)>f(y)一定成立
2. f(n)值相同的segments的数量不得超过M。
那么maybeMergeSegments()函数是如何确保这两个公式成立的呢?
1.首先,从代码:
protected final void maybeFlushRamSegments() throws IOException {
// A flush is triggered if enough new documents are buffered or
// if enough delete terms are buffered
if (ramSegmentInfos.size() >= minMergeDocs
|| numBufferedDeleteTerms >= maxBufferedDeleteTerms) {
flushRamSegments();
}
}
这儿minMergeDocs=maxBufferedDocs, 因此可以看出,当内存中缓存的segments被合并写回磁盘时生成的segment的document count等于或小于maxBufferedDocs(即minMergeDocs)。
补充:因为maybeMergeSegments()运行在同步代码中,因此只要ramSegmentInfos.size==minMergerDocs(即maxBufferedDocs)就会写回磁盘,因此应该不存在ramSegmentInfos.size>maxBufferedDocs才写回的情况。而且,但如果是这种情况,因为合并后的segment文件的document count过大,后面的maybeMergeSegments()将不做合并处理直接退出,上述公式就可能不成立,那么算法将有错。
----
2.
(1) 因此maybeMergeSegments()第一次执行时,所有segments的document count都小于等于maxBufferedDocs。此时,从i=0开始,合并i~i+mergeFactor-1个文件,如果合并后的doc count>maxBufferedDocs时,保留第i个segment,否则继续合并改变后的i~i+mergeFactor-1,直到doc count>maxBufferedDocs或所有segments文件个数已经<mergeFactor了。经过这样一轮的合并,除了最后<mergeFactor个的doc counts<=maxBufferedDocs文件外,其它文件的doc counts一定都>maxBufferedDocs并<maxBufferedDocs*mergeFactor了。
(2) 这时,不理会最后<mergeFactor个doc count<maxBufferedDocs的文件,而后按2.1的同理规则,合并之前的文件,让这些文件的最后<mergerFactor个segment符合 maxBufferedDocs<doc counts<=maxBufferedDocs*mergeFactor,之前的segment文件都符合maxBufferedDocs*mergeFactor<doc counts<=maxBufferedDocs*mergeFactor^2
(3) 重复2.2,最后得到的列表就会满足上述两等式的成立
3.之后,每次从内存缓存中flush出一个新的segment时,也就是往这个segments列表的最后增加一个doc_count<=maxBufferedDocs的文件,同样执行上述步骤2进行合并,能够始终保证上述两公式的成立。
4.
(1)IndexWriter#addIndexesNoOptimize同样借鉴使用了maybeMergeSegments()算法,区别此时,实际是已有一个符合两公式的segments序列T,在T之后追加上随意顺序的segments序列S。这时,我们先找到S中doc count值最大的那个segment,计算出它属于的区间f(x),使得maxBufferedDocs*mergeFactor^x<doc counts<=maxBufferedDocs*mergeFactor^(x+1),而后按2.2的算法合并出除了最后<mergerFactor个segments外, 之前所有segments都符合 a. doc count>maxBufferedDocs*mergeFactor^(x+1) b.符合上述2等式。
btw: 因为这儿调用IndexWriter#addIndexesNoOptimize传入的参数是maxBufferedDocs*mergeFactor^(x+1),因为S所有segment的doc count都一定小于maxBufferedDocs*mergeFactor^(x+1),因此S的所有元素都会参与收缩合并。
(2)将最后<mergerFactor个doc count<maxBufferedDocs*mergeFactor^(x+1)的segments合并,如果合并后的segment的doc count大于maxBufferedDocs*mergeFactor^(x+1),就继续2.2步骤,使得整个队列符合上述两公式
上述两种策略,最终确保了Lucene中的segments不会太多,确保效率。
BTW:实际上,如果documents太多时,Lucene还支持把docuements分成几个组,每个组用独立的进程或电脑进行索引,而后再多个目录的索引合并起来,具体可参考IndexWriter#addIndexesNoOptimize和IndexWriter#addIndexes函数
分享到:
相关推荐
域内的信息可以被独立索引,有不同的索引策略。例如,文本内容可能被分词,而日期或ID可能作为非分词字段处理。 在Lucene中,词(Term)是经过词法分析后的最小索引单元。正向信息(Forward Index)描述了从索引到...
Lucene采用了高效的内存管理策略,如使用RAMDirectory和MMapDirectory进行索引存储,以及使用FSDirectory进行磁盘操作。此外,通过缓存策略,可以减少磁盘I/O,提高查询速度。 8. **扩展性和插件化** Lucene具有...
- **IndexReader**和**IndexWriter**的优化选项,如合并策略和段合并大小控制。 7. **分布式搜索**: - **Solr**:基于Lucene的开源搜索服务器,支持分布式搜索和处理大量数据。 8. **扩展性和定制性**: - **...
在Lucene中,"Segments"是一个关键概念,它代表了索引的基本单位。当新的文档被添加到索引中,它们通常会被添加到一个新的Segment中,以避免对现有索引的修改。每个Segment包含了一组文档及其相关的倒排索引。...
- `org.apache.lucene.analysis.Analyzer`接口定义了如何将文档分解为一系列词汇单位(tokens),其具体实现类`StandardAnalyzer`采用标准分词策略。 #### 如何给文档评分 - **文档评分类Similarity**: - `org....
此外,索引的合并策略决定了何时和如何合并段(Segments),这对于控制索引碎片和优化读取性能是关键。 最后,章节可能会涉及一些实际示例和代码片段,帮助读者更好地理解和应用所学知识。这些示例可能涵盖了创建、...
7. **性能优化(Performance Optimization)**:通过调整内存缓冲区大小、使用NIO、合并策略等手段,可以显著提升Lucene的性能。源码会揭示这些优化技巧。 8. **分布式搜索(Distributed Search)**:如果你需要...
- **合并段(Merge Segments)**: `IndexWriter`会在索引中创建多个段,定期合并小段可以提高搜索效率。 - **缓存(Cache)**: 使用`BitSet`缓存和`FilterCache`可以加快查询速度,特别是对于经常查询的过滤条件。...
**Lucene技术详解** Lucene是一个高性能、全文检索库,由Apache软件基金会开发并维护,是Java编程语言中广泛使用的...在实际项目中,结合索引策略、缓存机制、查询优化等方法,可以进一步提升Lucene的性能和用户体验。
Lucene的实现基于文档、域(fields)、项(terms)和段(segments)的概念。文档是索引的基本单位,可以包含多个域,每个域对应不同的数据类型,如标题、内容、作者等。项是经过分词处理后的关键词,它们在文档中...
- Lucene的索引由多个片段(Segments)组成,每个片段是一个独立的索引单元。新添加的文档会创建新的片段,旧的片段可以通过合并来优化索引大小和性能。 4. **索引过程** - 数据转换:将非文本数据转化为纯文本...
6. 多线程和并发控制:在多线程环境中安全地操作Lucene索引。 7. 分布式搜索:通过Solr或Elasticsearch实现分布式搜索集群。 通过学习这些源代码,开发者不仅可以深入理解Lucene的工作机制,还能获得实际应用的经验...
Exception in thread "main" java.io.FileNotFoundException: no segments* file found in org.apache.lucene.store.FSDirectory@E:\index: files: at org.apache.lucene.index.SegmentInfos$FindSegmentsFile.run...
Lucene的索引由多个文件组成,包括段(Segments)、字段(Fields)、文档(Documents)和术语(Terms)。段是基本的搜索单位,每个段包含多条文档,每个文档又包含多个字段。字段可以设置为可搜索、可索引、可存储...
接着,系统会生成片段文件(segments),这些文件包含了待抓取的URL列表。Nutch的爬行器(Fetcher)根据这些URL从互联网上抓取网页内容。 抓取到的网页内容随后进行解析,提取出文本和数据。在这个过程中,Nutch会...
2. **分页处理**:Nutch将抓取的网页分割成多个块(segments),每个块包含一定数量的网页。这种设计便于在不同的阶段独立处理这些网页,例如,解析HTML、提取文本、构建索引等。 3. **HTML解析**:Nutch使用了开源...