Lucene多线程操作实现

shenhua 贡献于2011-07-21

作者 YlmF  创建于2011-01-24 08:43:00   修改者YlmF  修改于2011-01-24 08:44:00字数9964

文档摘要:Lucene多线程操作实现对于并发,Lucene遵循以下规则: 1.允许任意多的读操作并发,即任意数量用户可同时对同一索引做检索操作。2.即便正在进行索引修改操作(索引优化、添加文档、删除文档),依然允许任意多的检索操作并发执行。3.不允许并发修改操作,也就是说同一时间只允许一个索引修改操作。Lucene内部已经对多线程安全进行了处理,很多操作都使用了lock进行多线程同步锁定。只要遵循一定的规则,就可以在多线程环境下安全运行Lucene。 
关键词:

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 方法关闭。  代码不完善,仅供参考!需要做些修改才能应用于实际项目。  Java代码  1. //索引修改器的获取和关闭   2. import java.io.File;   3. import java.io.IOException;   4. import java.io.StringReader;   5. import java.util.ArrayList;   6. import java.util.HashMap;   7. import java.util.Map;   8. import org.apache.lucene.analysis.Analyzer;   9. import org.apache.lucene.analysis.standard.StandardAnalyzer;   10. import org.apache.lucene.index.CorruptIndexException;   11. import org.apache.lucene.index.IndexModifier;   12. import org.apache.lucene.store.Directory;   13. import org.apache.lucene.store.LockObtainFailedException;   14. import org.apache.lucene.store.RAMDirectory;   15.     16.     17. public class MyIndexModifier {   18.     private static Analyzer analyzer = new StandardAnalyzer();   19.     private static IndexModifier modifier;   20.     private static ArrayList threadList = new ArrayList();   21.     22.     private MyIndexModifier() { }   23.     24.     static final File INDEX_DIR = new File("D:/docindex");   25.       public static IndexModifier GetInstance()   26.       {   27.           synchronized (threadList)   28.         {   29.           if (modifier == null)   30.           {   31.             try {   32.                 modifier = new IndexModifier(INDEX_DIR, analyzer, false);   33.                 //索引性能测试参数配置   34.                 modifier.setMergeFactor(1000);    35.                  System.out.println("MergeFactor: " + modifier.getMergeFactor());   36.                  System.out.println("MaxBufferedDocs: " + modifier.getMaxBufferedDocs());   37.             } catch (CorruptIndexException e) {   38.                 e.printStackTrace();   39.             } catch (LockObtainFailedException e) {   40.                 e.printStackTrace();   41.             } catch (IOException e) {   42.                 e.printStackTrace();   43.             }   44.           }   45.     46.           if (!threadList.contains(Thread.currentThread()))   47.             threadList.add(Thread.currentThread());   48.     49.           return modifier;   50.         }   51.       }   52.     53.       public static void Close()   54.       {   55.           synchronized (threadList)   56.         {   57.           if (threadList.contains(Thread.currentThread()))   58.             threadList.remove(Thread.currentThread());   59.     60.           if (threadList.size() == 0)   61.           {   62.            try {   63.                if (modifier != null)   64.                 {   65.                     66.                 modifier.close();   67.                 modifier = null;   68.                 }   69.             } catch (CorruptIndexException e) {   70.                 e.printStackTrace();   71.             } catch (IOException e) {   72.                 e.printStackTrace();   73.             }   74.           }   75.         }   76.       }   77. }   78.     Java代码  1. //线程处理类   2. import java.io.IOException;   3. import java.util.Date;   4. import org.apache.log4j.LogManager;   5. import org.apache.log4j.Logger;   6. import org.apache.lucene.document.Document;   7. import org.apache.lucene.document.Field;   8. import org.apache.lucene.index.CorruptIndexException;   9. import org.apache.lucene.index.IndexModifier;   10. import org.apache.lucene.index.StaleReaderException;   11. import org.apache.lucene.store.LockObtainFailedException;   12. import com.miracle.dm.framework.common.TimestampConverter;   13.    14. public class  TestModifer extends Thread{   15.       16.     private static Logger logger =  LogManager.getLogger(TestModifer.class);   17.     @Override   18.     public void run() {   19.         IndexModifier writer = MyIndexModifier.GetInstance();   20.     21.          try {   22.                 writer.deleteDocument(0);   23.             } catch (StaleReaderException e1) {   24.                 // TODO Auto-generated catch block   25.                 e1.printStackTrace();   26.             } catch (CorruptIndexException e1) {   27.                 // TODO Auto-generated catch block   28.                 e1.printStackTrace();   29.             } catch (LockObtainFailedException e1) {   30.                 // TODO Auto-generated catch block   31.                 e1.printStackTrace();   32.             } catch (IOException e1) {   33.                 // TODO Auto-generated catch block   34.                 e1.printStackTrace();   35.             }   36.             37.         for (int x = 0; x < 10; x++)   38.         {   39.           Document doc = new Document();   40.           TimestampConverter converter = new TimestampConverter();   41.           Date date = new Date();   42.           String docDate = converter.timestampToShortStr(date);   43.           doc.add(new Field("docDate",  docDate  , Field.Store.YES, Field.Index.TOKENIZED));   44.          45.           try {   46.             writer.addDocument(doc);   47.         } catch (CorruptIndexException e) {   48.             // TODO Auto-generated catch block   49.             e.printStackTrace();   50.         } catch (LockObtainFailedException e) {   51.             // TODO Auto-generated catch block   52.             e.printStackTrace();   53.         } catch (IOException e) {   54.             // TODO Auto-generated catch block   55.             e.printStackTrace();   56.         }   57.         }   58.         logger.debug(""+ Thread.currentThread()+","+ writer.docCount());   59.         MyIndexModifier.Close(); // 注意不是调用 IndexModifier.Close() !   60.     }   61. }   Java代码  1. 多线程测试代码   2. import java.io.Console;   3. import java.io.IOException;   4. import java.util.Date;   5.     6. import org.apache.log4j.LogManager;   7. import org.apache.log4j.Logger;   8. import org.apache.lucene.document.Document;   9. import org.apache.lucene.document.Field;   10. import org.apache.lucene.index.CorruptIndexException;   11. import org.apache.lucene.index.IndexModifier;   12. import org.apache.lucene.store.LockObtainFailedException;   13. import com.miracle.dm.framework.common.TimestampConverter;   14.     15. public class test {   16.     private static Logger logger =  LogManager.getLogger(test.class);    17.       18.     public test(){   19.     }   20.       21.     /**  22.      * @param args  23.      */   24.     public static void main(String[] args) {   25.           26.         for (int i = 0; i < 100; i++)   27.         {   28.           new TestModifer().start();   29.         }   30.     }   31. }   注意:使用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  Java代码  1.  public class Product  {           2.  @SearchableId       3. private Integer id;      4.  private Category category;      5.  private String name;      6.  private Double unitprice;      7.  @SearchableProperty(name = "name")     8.   public String getName() {        9.    return this.name;      10.  }       11. @SearchableComponent (refAlias = "category")     12.   public Category getCategory() {       13. return this.category;      14.  }       15. public Double getUnitprice() {     16.       return this.unitprice;    17.    }   18.     3. 与spring,hibernate集成配置  3.1 spring配置文件  hiberante中的sessionFactory,transactionManager相比大家也是轻车熟路了.这里还是带过(因为不牵扯稿费的问题吗^_^ ).compass已经对对spring集成做了很好的封装,让我们的使用更加简单,我们可以不为compass编写一行代码,就可以做完搜索引擎的检索.下面是compass在spring中的简明配置. 详情点击查看springside中的applicationContext-lucene.xml  :  Xml代码  1.    2.    3.    4.    5.         6.      7.         8.         org.springside.bookstore.domain.Book   9.         10.      11.    12.      13.    14.       15.            16.             file://${user.home}/springside/compass   17.             org.compass.spring.transaction.SpringSyncTransactionFactory   18.            19.        20.    21.       22.     23.      24.         25.         hibernateDevice   26.         27.         28.     29.    30.      31.      32.          33.              34.          35.      36.     37.    上面要留意的配置有:  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    Xml代码  1.       2.         3.         4.           5.        6.       7.         8.    3.3 View JSP    简单搜索页面:只需要一个query 参数:    结果页面:  结果页面将返回几个变量,包括:  searchResults(搜索结果) 包括hits(结果)和 searchtime(耗时)      pages(分页信息) 包括page_from page_to等      command(原来的查询请求)  具体使用见springside的advancedSearch.jsp ,下面是简版的代码:  Jsp代码  1.         耗时:   2. ms                     3.                           4.                             5.                                   6.       《${hit.data.name}》                                    7.   
 作者:${hit.data.author}
                      8.    
                       9.                  10.            11.    12.    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的做法,完全自行扩展。

下载文档到电脑,查找使用更方便

文档的实际排版效果,会与网站的显示效果略有不同!!

需要 15 金币 [ 分享文档获得金币 ] 4 人已下载

下载文档