`
wbj0110
  • 浏览: 1603104 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

lucene4.5源码分析系列:索引缓存以及刷新

阅读更多

缓存和刷新是比较重要的问题,它涉及到lucene如何管理内存和磁盘。前面提到索引的结果是缓存在内存中的,等到一定时候才会将其刷新到硬盘上去。缓存在这里的目的无非是缓解高速设备到低速设备的不匹配。下面这些问题都比较重要:调用增删改索引后此时索引时已经写入磁盘还是仍然驻留内存,即索引的刷新时间是什么?其次,缓存会占用多少内存?另外,刷新的效率如何?最后,lucene允许多个线程并发刷新索引,具体实现是怎么做的?

  flush的一个总入口是DocumentWriter中的doFlush,随后严格按照索引链层层向下传递,直到FreqProxTermWriter的flush方法,FreqProxTermWriter中最后会调用如下语句,将所有对索引文件的操作交给codec去做。

 

[java] view plaincopy
 
  1. state.segmentInfo.getCodec().postingsFormat().fieldsConsumer(state).write(fields);  

  那么到底哪些地方会调用flush呢?实际上,下面这5个地方都会最终调用到flush,可以发现,这5个地方已经涵盖了对索引的大部分操作,可见flush的重要。

 

  从源码上看,addIndexes, forceMerge, close和commit4个操作都是进行full flush,入口为DocumentWriter.flushAllThreads,他们会对所有active的DocumentsWriterPerThread进行flush;而updateDocument则只会flush一个DocumentsWriterPerThread。

  在lucene中,flush是由DocumentsWriterFlushControl来统一控制的,好处就是统一入口,便于维护和管理。这个控制器用了很多方式来保持其扩展灵活性以及健壮性。

 刷新时机策略

  DocumentsWriterFlushControl中有一个FlushPolicy。我们每次更新文档都需要将缓存内容写入磁盘吗?如果每次索引的内容只有几十字节,那么频繁写入其实是不划算的,会带来极大的开销,也就失去使用缓存的意义了。FlushPolicy就是定义了这个刷新时机的策略,默认策略在FlushByRamOrCountsPolicy中定义。如下图,我们可以看出来,FlushByRamOrCountsPolicy中实际上有3种方式来控制缓存的刷新。

  其中flushOnRAM会在删除或者插入时被应用,也就是内存达到设定值时候才允许刷新;flushOnDocCount则只在插入时生效,即文档数达到设定值生效;flushOnDeleteTerms在删除时的缓存大于设定值时生效;而这3个设定值分别对应indexWriterConfig中的RAMBufferSizeMB,MaxBufferedDocs, MaxBufferedDeleteTerms。也就是说,我们可以通过控制这几个值来控制刷新的方式。因而一旦设定了RAMBufferSizeMB这种刷新方式,则缓存占用的最大内存就是RAMBufferSizeMB

 

[java] view plaincopy
 
  1. public void onDelete(DocumentsWriterFlushControl control, ThreadState state) {  
  2.   if (flushOnDeleteTerms()) {  
  3.     // Flush this state by num del terms  
  4.     final int maxBufferedDeleteTerms = indexWriterConfig  
  5.         .getMaxBufferedDeleteTerms();  
  6.     if (control.getNumGlobalTermDeletes() >= maxBufferedDeleteTerms) {  
  7.       control.setApplyAllDeletes();  
  8.     }  
  9.   }  
  10.   if ((flushOnRAM() &&  
  11.       control.getDeleteBytesUsed() > (1024*1024*indexWriterConfig.getRAMBufferSizeMB()))) {  
  12.     control.setApplyAllDeletes();  
  13.    if (infoStream.isEnabled("FP")) {  
  14.      infoStream.message("FP""force apply deletes bytesUsed=" + control.getDeleteBytesUsed() + " vs ramBuffer=" + (1024*1024*indexWriterConfig.getRAMBufferSizeMB()));  
  15.    }  
  16.  }  
  17. }  
  18.   
  19. public void onInsert(DocumentsWriterFlushControl control, ThreadState state) {  
  20.   if (flushOnDocCount()  
  21.       && state.dwpt.getNumDocsInRAM() >= indexWriterConfig  
  22.           .getMaxBufferedDocs()) {  
  23.     // Flush this state by num docs  
  24.     control.setFlushPending(state);  
  25.   } else if (flushOnRAM()) {// flush by RAM  
  26.     final long limit = (long) (indexWriterConfig.getRAMBufferSizeMB() * 1024.d * 1024.d);  
  27.     final long totalRam = control.activeBytes() + control.getDeleteBytesUsed();  
  28.     if (totalRam >= limit) {  
  29.       if (infoStream.isEnabled("FP")) {  
  30.         infoStream.message("FP""flush: activeBytes=" + control.activeBytes() + " deleteBytes=" + control.getDeleteBytesUsed() + " vs limit=" + limit);  
  31.       }  
  32.       markLargestWriterPending(control, state, totalRam);  
  33.     }  
  34.   }  
  35. }  

  健康管理
   在多线程下,有可能出现索引线程的速度快于刷新线程的速度,积累下去很容易导致JVM内存耗尽。

 

   DocumentsWriterFlushControl中的DocumentsWriterStallControl便是用来做健康管理的。所谓的健康管理,就是在索引线程速度过快时阻塞索引线程,从而保证内存的占用不会持续上涨。判断是否需要健康管理的程序如下

 

[java] view plaincopy
 
  1. final boolean stall = ((activeBytes + flushBytes) > limit)  &&  
  2.                           (activeBytes < limit) &&  
  3.                           !closed;  

   FlushAllThreads

   具体到一次完全刷新具体做了什么事,可以参考下图,实际上主要就是3件事,首先做一些准备工作,因为刷新是非常复杂的过程,当中涉及多线程操作以及状态的变化,所以需要mark,接下来对每个pending状态的DocumentsWriterPerThread做flush,最后是等待所有flush结束。

 

  markForFullFlush这个方法实际上有如下这些步骤。 实际上这个方法有许多多线程处理的技巧,都是为了尽可能的少用锁。

   1. 确定删除队列。这里会新建一个generation加1的删除队列来替换原来的删除队列,而后续操作则使用老的删除队列,这么做无非是想在fullFlush时可以同时做其他操作比如删除而不用整个锁住。

   2. 改变每个待刷新的ThreadState的状态,从active变为flushing。这步其实还有一个最重要的操作,就是internalTryCheckOutForFlush,他的机制就是将所有checkout的ThreadState放到一个Map(flushingWriters)中,这样就类似上了锁,只要在Map中的都是已经加锁的,比每次加锁判断要高效。

   3. 对blockedFlushes阻塞的每个DocumentWriterPerThread,将其状态设置为flushing并从blockedFlushes中删除。

   4. 准备好flushQueue,主要就是将上面步骤中fullFlushBuffer搜集到的DocumentWriterPerThread全部填充到flushQueue中

   5. 更新健康状态,这步就是去判断是否需要健康管理。

  如图,实际的刷新工作是多个线程并发的做,他们都会调用doFlush,很有意思的一点是它会把每次刷新作为一个ticket放到ticketQueue中来管理,这样会有一个统一地方获知正在刷新的ticket,也便于上锁等操作。在flush结束时会进入doAfterFlush,这里会将前面的flushingWriters删除本次flush用的DocumentsWriterPerThread,然后更新健康管理的状态并且使用notifyAll来通知其他等待的线程(主要就是waitForFlush的等待线程)。至于doFlush中实际如何顺着索引链向下的调用,这里就不细细说明了,整体流程到这里已经比较清楚,需要细节可以自行查阅相关代码。

  waitForFlush是主线程与刷新线程同步的过程,每次都判断flushingWriters这个Map是否没有可用的threadState(即前面的flush有没有完成),如果没有完成,则继续等待。这正是《java多线程设计模式》中的Balking的使用。

http://blog.csdn.net/liweisnake/article/details/11659799

 
分享到:
评论

相关推荐

    lucene3源码分析

    ### Lucene3源码分析知识点概述 #### 一、全文检索的基本原理 ##### 1. 总论 全文检索系统是一种高效的信息检索技术,能够帮助用户在海量文档中快速找到包含特定关键词的信息。Lucene是Java领域内最受欢迎的全文...

    Lucene 3.0 原理与代码分析PDF

    Lucene学习总结之四:Lucene索引过程分析(1) Lucene学习总结之四:Lucene索引过程分析(2) Lucene学习总结之四:Lucene索引过程分析(3) Lucene学习总结之四:Lucene索引过程分析(4) www.chinaandroid.com

    IK分词器集成lucene4.5使用方法

    在本教程中,我们将探讨如何将IK分词器集成到Lucene 4.5版本中,以提升中文文本处理的效率和准确性。 首先,让我们了解一下IKAnalyzer。IKAnalyzer是由国人开发的一款开源中文分词库,它具有较好的分词效果和较高的...

    SSI集成lucene4.5使用案例

    6. **优化性能**:根据需求,可以考虑使用多线程并行索引、缓存策略、近实时搜索(NRT)等技术提高系统性能。例如,使用`NRTManager`可以实现快速响应的增量索引。 7. **安全与权限**:确保索引和搜索过程符合网站...

    Lucene学习源码.rar

    《深入剖析Lucene:从源码到实践》 Lucene是一个高性能、全文本搜索库,由Apache软件基金会开发,主要用于构建搜索引擎。它提供了一个简单但功能强大的API,使得开发者能够轻松地在应用中实现文本检索功能。Lucene...

    Lucene3.5源码jar包

    10. **性能调优**:通过分析源码,开发者可以了解到如何调整各种参数,如缓存大小、合并策略等,来优化Lucene的性能。 总的来说,深入学习Lucene 3.5.0的源码,可以帮助开发者掌握全文检索的核心技术,了解其内部...

    lucene+jdbcTemplate封装API+缓存实现索引精确刷新

    在IT行业中,搜索引擎技术是数据检索的关键组成部分,而Apache Lucene是一个开源的全文搜索引擎库,提供了高效的文本分析和索引功能。在这个项目中,我们看到开发者利用Lucene结合jdbcTemplate来封装API,并引入缓存...

    lucene.net 2.9.1 源码

    《深入剖析Lucene.NET 2.9.1:源码解析与应用开发》 Lucene.NET 2.9.1是开源搜索引擎库Lucene的.NET版本,它为.NET开发者提供了强大的全文检索和索引功能。这个版本的源码提供了一个宝贵的资源,帮助我们理解其内部...

    lucene-core-7.7.0-API文档-中文版.zip

    赠送jar包:lucene-core-7.7.0.jar; 赠送原API文档:lucene-core-7.7.0-javadoc.jar; 赠送源代码:lucene-core-7.7.0-sources.jar; 赠送Maven依赖信息文件:lucene-core-7.7.0.pom; 包含翻译后的API文档:lucene...

    基于lucene技术的增量索引

    Lucene通过分析这些文本,将其拆分为术语,并在倒排索引中存储每个术语的位置信息,以便快速定位到包含特定术语的文档。 **2. 增量索引的概念** 增量索引的目的是避免重新构建整个索引,尤其是在大型数据集上,这...

    Lucene 索引的简单使用

    以上就是关于“Lucene索引的简单使用”的详细介绍,包括其核心概念、创建和查询索引的步骤以及一些高级特性。希望对你理解和应用Lucene有所帮助。在实际开发中,可以根据需求选择合适的Analyzer,优化索引策略,以...

    lucene5.0源码包

    Lucene 5.0的API更加精炼和直观,主要分为几个核心接口:`IndexWriter`用于创建和更新索引,`IndexReader`用于读取索引,`Directory`代表存储索引的物理位置,`Analyzer`处理文本分析,`Query`表示查询条件,`...

    lucene索引查看工具及源码

    通过阅读和学习 Luke 的源码,我们可以了解到如何与 Lucene 索引进行交互,以及索引结构是如何组织和存储的。 在提供的压缩包 "luke-3.3.0" 中,包含了 Luke 工具的旧版本。这个版本可能不支持最新的 Lucene 版本,...

    lucene jar 包

    《深入理解Lucene 4.5:核心概念与应用实践》 Lucene是一个高性能、全文检索库,由Apache软件基金会开发并维护。它为Java开发者提供了强大的文本搜索功能,是构建搜索引擎的重要工具。本文将围绕Lucene 4.5版本进行...

    lucene源码分析1111

    在这个主题中,我们将对Lucene的源码进行深入的分析。 1. **Lucene的基本结构** - Lucene的核心组件包括索引(Index)、查询解析器(Query Parser)、搜索器(Searcher)和分词器(Tokenizer)。索引是搜索引擎的...

    lucene5 源码教程

    《Lucene5源码教程:拼音检索与分词器实战》 在当今的信息化社会,搜索引擎已经成为我们获取信息的重要工具。Lucene,作为Apache软件基金会的开源全文检索库,为开发者提供了强大的文本检索功能。本教程将深入探讨...

    wangwangla#qiuzhao#第三章 Lucene的源码分析1

    反向信息词---&gt;文档文件有:数据类型:Lucene索引文件中Byte:最基本的数据类型Uint32:由4个byte组成VInt:长度变化的数据类型,可能包含多

    lucene 对 xml建立索引

    ### Lucene对XML文档建立索引的技术解析与实践 #### 一、引言 随着互联网技术的迅猛发展,非结构化数据(如XML文档)在企业和组织中的应用日益广泛。如何高效地处理这些非结构化的数据,特别是进行快速检索成为了一...

    Lucene.Net源码与说明文档

    Lucene.Net 的源码结构清晰,分为多个模块,如索引、查询解析、分词器等,每个模块都有明确的职责。源码主要由 C# 编写,遵循面向对象的设计原则,包括封装、继承和多态。通过阅读源码,开发者可以深入了解搜索引擎...

Global site tag (gtag.js) - Google Analytics