`
zhaoshg
  • 浏览: 253246 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Lucene多线程操作实现

阅读更多
对于并发,Lucene 遵循以下规则:

  • 1. 允许任意多的读操作并发,即任意数量用户可同时对同一索引做检索操作。
  • 2. 即便正在进行索引修改操作(索引优化、添加文档、删除文档),依然允许任意多的检索操作并发执行。
  • 3. 不允许并发修改操作,也就是说同一时间只允许一个索引修改操作。


Lucene内部已经对多线程安全进行了处理,很多操作都使用了 lock 进行多线程同步锁定。只要遵循一定的规则,就可以在多线程环境下安全运行 Lucene。
方案一:
建议:

  • 1. Directotry、Analyzer 都是多线程安全类型,只需建立一个 Singleton 对象即可。
  • 2. 所有线程使用同一个 IndexModifier 对象进行索引修改操作。
  • 3. IndexWriter/IndexReader/IndexModifier/IndexSearcher 最好使用同一个 Directory 对象,否则多线程并发读写时可能引发 FileNotFoundException。


IndexModifier 对象封装了 IndexWriter 和 IndexReader 的常用操作,其内部实现了多线程同步锁定。使用 IndexModifier 可避免同时使用 IndexWriter 和 IndexReader 时需要在多个对象之间进行同步的麻烦。等所有修改操作完成后,记住调用 Close() 方法关闭相关资源。并不是每次操作都需要调用 Optimize(),可以依据特定情况,定期执行优化操作。

以下演示代码简单封装了一个 IndexModifier Signleton 类型,确保多线程使用同一个对象,且只能由最后一个多线程调用 Close 方法关闭。
代码不完善,仅供参考!需要做些修改才能应用于实际项目。
//索引修改器的获取和关闭
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexModifier;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.LockObtainFailedException;
import org.apache.lucene.store.RAMDirectory;
 
 
public class MyIndexModifier {
    private static Analyzer analyzer = new StandardAnalyzer();
    private static IndexModifier modifier;
    private static ArrayList<Thread> threadList = new ArrayList<Thread>();
 
    private MyIndexModifier() { }
 
    static final File INDEX_DIR = new File("D:/docindex");
      public static IndexModifier GetInstance()
      {
          synchronized (threadList)
        {
          if (modifier == null)
          {
            try {
                modifier = new IndexModifier(INDEX_DIR, analyzer, false);
                //索引性能测试参数配置
                modifier.setMergeFactor(1000); 
                 System.out.println("MergeFactor: " + modifier.getMergeFactor());
                 System.out.println("MaxBufferedDocs: " + modifier.getMaxBufferedDocs());
            } catch (CorruptIndexException e) {
                e.printStackTrace();
            } catch (LockObtainFailedException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
          }
 
          if (!threadList.contains(Thread.currentThread()))
            threadList.add(Thread.currentThread());
 
          return modifier;
        }
      }
 
      public static void Close()
      {
          synchronized (threadList)
        {
          if (threadList.contains(Thread.currentThread()))
            threadList.remove(Thread.currentThread());
 
          if (threadList.size() == 0)
          {
           try {
               if (modifier != null)
                {
                 
                modifier.close();
                modifier = null;
                }
            } catch (CorruptIndexException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
          }
        }
      }
}
 


//线程处理类
import java.io.IOException;
import java.util.Date;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexModifier;
import org.apache.lucene.index.StaleReaderException;
import org.apache.lucene.store.LockObtainFailedException;
import com.miracle.dm.framework.common.TimestampConverter;

public class  TestModifer extends Thread{
   
    private static Logger logger =  LogManager.getLogger(TestModifer.class);
    @Override
    public void run() {
        IndexModifier writer = MyIndexModifier.GetInstance();
 
         try {
                writer.deleteDocument(0);
            } catch (StaleReaderException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (CorruptIndexException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (LockObtainFailedException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
         
        for (int x = 0; x < 10; x++)
        {
          Document doc = new Document();
          TimestampConverter converter = new TimestampConverter();
          Date date = new Date();
          String docDate = converter.timestampToShortStr(date);
          doc.add(new Field("docDate",  docDate  , Field.Store.YES, Field.Index.TOKENIZED));
      
          try {
            writer.addDocument(doc);
        } catch (CorruptIndexException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (LockObtainFailedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        }
        logger.debug(""+ Thread.currentThread()+","+ writer.docCount());
        MyIndexModifier.Close(); // 注意不是调用 IndexModifier.Close() !
    }
}


多线程测试代码
import java.io.Console;
import java.io.IOException;
import java.util.Date;
 
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexModifier;
import org.apache.lucene.store.LockObtainFailedException;
import com.miracle.dm.framework.common.TimestampConverter;
 
public class test {
    private static Logger logger =  LogManager.getLogger(test.class); 
   
    public test(){
    }
   
    /**
     * @param args
     */
    public static void main(String[] args) {
       
        for (int i = 0; i < 100; i++)
        {
          new TestModifer().start();
        }
    }
}

注意:使用lucene现在的新版本的朋友一定会发现,现在并不推荐使用。而查看API发现IndexModifier已经被IndexWriter代替。再查看IndexWriter,其中提供了新增,删除,更新索引文档的方法。

这里是自己编码来实现,但是我不知道当几千或更多用户在对索引进行操作,那会不会导致close长时间没有运行,而无法检索到最新的更新索引。希望大家帮我考虑一下是否会存在这方面的问题,如果存在该如何解决?

方案二:
利用已有的lucene框架,例如compass
它对lucene实现了实时索引。可基于hibernate,当更新数据库时,系统会自动更新索引。
·              Compass将lucene、Spring、Hibernate三者结合
转自:http://wemyss.blogbus.com/logs/8014799.html
1.概述
Compass将lucene、Spring、Hibernate三者的起来,以很低很低的成本快速实现企业应用中的搜索功能。

HomePage: http://www.opensymphony.com/compass/

springside里用了compass来做图书搜索,快速建立的流程如下:

  • 1.用简单的compass annotation把Book对象映射到Lucene。
  • 2.配置compass默认提供的基于Spring MVC的Index Controller 和Search Controller。
  • 3.编写查询结果的显示页面,将controller返回的变量显示出来。


2.Object/Search Engine Mapping的 Annotations配置
使用JDK5 的annotation 来进行OSEM(Object/Search Engine Mapping)比用xml文件按简单许多,下面就是简单的搜索类,可见@SearchableID, @SearchableProperty与@SearchableComponent 三个标记,分别代表主键、可搜索的属性与关联的,另一个可搜索的对象,另外Compass要求POJO要有默认构造函数,要实现equals()和hashcode():

详细请点击查看springside中的Product.java ,  Book.java, Category.java

 public class Product  {        
 @SearchableId    
private Integer id;   
 private Category category;   
 private String name;   
 private Double unitprice;   
 @SearchableProperty(name = "name")  
  public String getName() {     
   return this.name;   
 }    
@SearchableComponent (refAlias = "category")  
  public Category getCategory() {    
return this.category;   
 }    
public Double getUnitprice() {  
      return this.unitprice; 
   }
 


3. 与spring,hibernate集成配置
3.1 spring配置文件
hiberante中的sessionFactory,transactionManager相比大家也是轻车熟路了.这里还是带过(因为不牵扯稿费的问题吗^_^ ).compass已经对对spring集成做了很好的封装,让我们的使用更加简单,我们可以不为compass编写一行代码,就可以做完搜索引擎的检索.下面是compass在spring中的简明配置. 详情点击查看springside中的applicationContext-lucene.xml  :

<beans>
<bean id="annotationConfiguration"          class="org.compass.annotations.config.CompassAnnotationsConfiguration"></bean>

<bean id="compass" class="org.compass.spring.LocalCompassBean">
   <!-- anontaition式设置     -->  
  <property name="classMappings">
     <list>
        <value>org.springside.bookstore.domain.Book</value>
     </list>
  </property>

  <property name="compassConfiguration" ref="annotationConfiguration"/>

   <property name="compassSettings">
        <props>
            <prop key="compass.engine.connection">file://${user.home}/springside/compass</prop>
            <prop key="compass.transaction.factory">org.compass.spring.transaction.SpringSyncTransactionFactory</prop>
        </props>
    </property>

   <property name="transactionManager" ref="transactionManager"/>
 </bean>
  <bean id="hibernateGpsDevice" class="org.compass.spring.device.hibernate.SpringHibernate3GpsDevice">
     <property name="name">
        <value>hibernateDevice</value>
     </property>
     <property name="sessionFactory" ref="sessionFactory"/>
 </bean>
<bean id="compassGps" class="org.compass.gps.impl.SingleCompassGps" init-method="start" destroy-method="stop">
  <property name="compass" ref="compass"/>
  <property name="gpsDevices">
      <list>
          <ref local="hibernateGpsDevice"/>
      </list>
  </property>
 </bean>
</beans>

上面要留意的配置有:

annotationConfiguration: 使用annotation配置,指定要转换的POJO如Book
compass.engine.connection : 索引文件在服务器上的存储路径.
hibernateGpsDevice: 与hibernate的绑定,用Hibernate 3 事件系统,支持Real Time Data Mirroring .经Hiberante的数据改变会自动被反射到索引里面.

3.2 web Controller的配置
两个Controller都是现成的,只要配置相关选项即可。
详情请查看springside的bookstore-servlet.xml

 
<bean id="indexBookController" class="org.compass.spring.web.mvc.CompassIndexController">   
  <property name="compassGps" ref="compassGps"/>   
  <property name="indexView" value="/admin/indexBook.jsp"/>   
  <property name="indexResultsView" value="/admin/indexBook.jsp"/> </bean> <bean id="searchBookController" class="org.compass.spring.web.mvc.CompassSearchController">   
 <property name="compass" ref="compass"/>   
 <propertyname="searchView"value="/home/top.jsp"/>  
 <property name="searchResultsView" value="/home/searchBook.jsp"/>    <property name="pageSize" value="5"/>
</bean>


3.3 View JSP
  简单搜索页面:只需要一个query 参数:
<INPUT type="text" size="20" name="query">
结果页面:

结果页面将返回几个变量,包括:

searchResults(搜索结果) 包括hits(结果)和 searchtime(耗时)
    pages(分页信息) 包括page_from page_to等
    command(原来的查询请求)

具体使用见springside的advancedSearch.jsp ,下面是简版的代码:


<c:if test="${not empty searchResults}">        耗时:
<c:out value="http://www.zhmy.com/${searchResults.searchTime}"/>ms      <c:forEach var="hit" items="${searchResults.hits}">            
    <c:choose>                   
 <c:when test="${hit.alias == 'book'}">                        
  <div class="left_content">                             
     <a href="#" class= "title"> 《${hit.data.name}》</a>                                 
  <br/> 作者:${hit.data.author}<br/>                   
   </div>                    
 </c:when>             
</c:choose>        
</c:forEach>
</c:if>


4.扩展高级搜索
扩展高级搜索其实很简单,SpringSide已经初步封装了加入包含以下任意单词,不包含以下任何单词,分类选择等条件及每页显示条数的确定。

如果需要更多条件:

  • 1. 加强搜索页面,加入更多条件的显示。
  • 2. 扩展compass的command class,接受从搜索条件页传过来的条件。 可从springside的AdvancedSearchCommand  扩展或从Compass的原类扩展。
  • 3. 扩展compass的searchController, 将command中的变量重新处理为一个符合Lucene语法规则的query变量 即可(见springside中的AdvancedSearchController ),同时可以为搜索条件页查询图书分类列表一类的变量。


   你可以从springside的AdvancedSearchController扩展,重载onSetupCommand (),参考父类的做法,加装自己的条件。重载referenceData(),把图书分类列表这种条件加入到AdvancedSearchCommand 的referenceData Map中供搜索条件页显示,例子见BookSearchController。

也可以参考BookSearchController和AdvancedSearchController的做法,完全自行扩展。
分享到:
评论

相关推荐

    重要Lucene多线程操作实现.pdf

    重要Lucene多线程操作实现.pdf

    【重要】Lucene多线程操作实现.pdf

    【重要】Lucene多线程操作实现.pdf

    【重要】Lucene多线程操作实现[定义].pdf

    【重要】Lucene多线程操作实现[定义].pdf

    基于lucene的搜索引擎总结

    IndexWriter和IndexReader是线程安全的,可以被多线程共享 全文索引/搜索 中文分词器 最大匹配法(机械分词):按照一定的策略将待分析的汉字串与一个“充分大的”机器词典中的词条进行配,若在词典中找到某个字符串...

    Lucene中文分词组件 JE-Analysis 1.5.1

    //增加一个新词典,采用每行一个词的读取方式(注意:多线程状态下此时的分词效果将不可预料) MMAnalyzer.addDictionary(reader); //增加一个新词 MMAnalyzer.addWord(newWord); //删除词库中的全部...

    HubbleDotNet 全文检索

    HubbleDotNet 设计了较为完善的并发控制程序,数据的增删改查可以多线程同时并发进行,没有任何冲突。HubbleDotNet 还进行 了缓存和内存管理设计,可以帮助用户最大限度的提高查询的效率。HubbleDotNet 力争在未来的...

    Hubble.net全文搜索源码

    Hubble.net 设计了较为完善的并发控制程序,数据的增删改查可以多线程同时并发进行,没有任何冲突。Hubble.net 还进行了缓存和内存管理设计,可以帮助用户最大限度的提高查询的效率。Hubble.net 力争在未来的几年内...

    数据结构算法

    Parallel的使用 多线程系列(5)5天不再惧怕多线程——第五天 线程池 5天不再惧怕多线程——第四天 信号量 5天不再惧怕多线程——第三天 互斥体 5天不再惧怕多线程——第二天 锁机制 5天不再惧怕多线程——第一天 尝试...

    java开源包3

    MyBatchFramework 是一个开源的轻量级的用以创建可靠的易管理的批量作业的Java包,主要特点是多线程、调度、JMX管理和批量执行报表,执行历史等。 SIP协议包 jSIP.tar jSIP这个Java包目标是用Java实现SIP(SIP:...

    java开源包4

    MyBatchFramework 是一个开源的轻量级的用以创建可靠的易管理的批量作业的Java包,主要特点是多线程、调度、JMX管理和批量执行报表,执行历史等。 SIP协议包 jSIP.tar jSIP这个Java包目标是用Java实现SIP(SIP:...

    java开源包1

    MyBatchFramework 是一个开源的轻量级的用以创建可靠的易管理的批量作业的Java包,主要特点是多线程、调度、JMX管理和批量执行报表,执行历史等。 SIP协议包 jSIP.tar jSIP这个Java包目标是用Java实现SIP(SIP:...

    java开源包11

    MyBatchFramework 是一个开源的轻量级的用以创建可靠的易管理的批量作业的Java包,主要特点是多线程、调度、JMX管理和批量执行报表,执行历史等。 SIP协议包 jSIP.tar jSIP这个Java包目标是用Java实现SIP(SIP:...

    java开源包2

    MyBatchFramework 是一个开源的轻量级的用以创建可靠的易管理的批量作业的Java包,主要特点是多线程、调度、JMX管理和批量执行报表,执行历史等。 SIP协议包 jSIP.tar jSIP这个Java包目标是用Java实现SIP(SIP:...

    java开源包6

    MyBatchFramework 是一个开源的轻量级的用以创建可靠的易管理的批量作业的Java包,主要特点是多线程、调度、JMX管理和批量执行报表,执行历史等。 SIP协议包 jSIP.tar jSIP这个Java包目标是用Java实现SIP(SIP:...

    java开源包5

    MyBatchFramework 是一个开源的轻量级的用以创建可靠的易管理的批量作业的Java包,主要特点是多线程、调度、JMX管理和批量执行报表,执行历史等。 SIP协议包 jSIP.tar jSIP这个Java包目标是用Java实现SIP(SIP:...

    java开源包10

    MyBatchFramework 是一个开源的轻量级的用以创建可靠的易管理的批量作业的Java包,主要特点是多线程、调度、JMX管理和批量执行报表,执行历史等。 SIP协议包 jSIP.tar jSIP这个Java包目标是用Java实现SIP(SIP:...

    java开源包8

    MyBatchFramework 是一个开源的轻量级的用以创建可靠的易管理的批量作业的Java包,主要特点是多线程、调度、JMX管理和批量执行报表,执行历史等。 SIP协议包 jSIP.tar jSIP这个Java包目标是用Java实现SIP(SIP:...

    java开源包7

    MyBatchFramework 是一个开源的轻量级的用以创建可靠的易管理的批量作业的Java包,主要特点是多线程、调度、JMX管理和批量执行报表,执行历史等。 SIP协议包 jSIP.tar jSIP这个Java包目标是用Java实现SIP(SIP:...

    java开源包9

    MyBatchFramework 是一个开源的轻量级的用以创建可靠的易管理的批量作业的Java包,主要特点是多线程、调度、JMX管理和批量执行报表,执行历史等。 SIP协议包 jSIP.tar jSIP这个Java包目标是用Java实现SIP(SIP:...

    java开源包101

    MyBatchFramework 是一个开源的轻量级的用以创建可靠的易管理的批量作业的Java包,主要特点是多线程、调度、JMX管理和批量执行报表,执行历史等。 SIP协议包 jSIP.tar jSIP这个Java包目标是用Java实现SIP(SIP:...

Global site tag (gtag.js) - Google Analytics