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,當更新資料庫時,系統會自動更新索引。

1.概述

Compass將lucene、Spring、Hibernate三者的起來,以很低很低的成本快速實現企業應用中的搜尋功能。

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: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執行操作實現

1. 允許任意多的讀操作併發,即任意數量使用者可同時對同一索引做檢索操作。2. 即便正在進行索引修改操作(索引優化、新增文件、刪除文件),依然允許任意多的檢索操作併發執行。3. 不允許併發修改操作,也就是說同一時間只允許一個索引修改操作。 Lucene內部已經對多執行緒安全進

【Dr.Chen的系列問題】Java執行實現操作

一、什麼是多執行緒? 多執行緒(英語:multithreading),是指從軟體或者硬體上實現多個執行緒併發執行的技術。具有多執行緒能力的計算機因有硬體支援而能夠在同一時間執行多於一個執行緒,進而提升整體處理效能。具有這種能力的系統包括對稱多處理機、多核心處理器以及晶片級多

BackgroundWorker 實現執行操作

背景介紹   在做程式的過程中,我們很可能遇到這樣的情況:當我們執行一個比較耗時的操作,即介面載入資料量略大的時,在該操作未完成之前再去操作介面,就會出現停止響應的情況,這稱為介面假死狀態,那一個小圓圈轉呀轉的,想必大家看著就頭疼。當然這是一個非常影響使用者體驗度的地方。

通過實現runnable實現執行操作

第一步:建立一個抽象類,實現runnable介面。 public abstract class ThreadRun implements Runnable { @Override public void run() { doSomeThing();

Thread三種實現&執行操作同一物件的互斥同步以及物件的同步&定時器Timer

多執行緒 程序 程序:(Process)是計算機中的程式關於某資料集合上的一次執行活動,是系統進行資源分配和排程的基本單位,是作業系統結構的基礎。在程序是程式的基本執行實體,在當代面向執行緒設計的計算機結構中,程序是執行緒的容器,程是程式的實體。 多執行緒:是指從

【OS】PV操作-理髮師問題-VC++執行模擬實現

自己除錯的,最近學OS,感覺還是用除錯除錯PV操作才踏實! #include "stdafx.h" #include "windows.h" #include "process.h" #include "iostream.h" #define N 5 #define M

一行 Python 實現並行化 -- 日常執行操作的新思路

春節坐在回家的火車上百無聊賴,偶然看到 Parallelism in one line 這篇在 Hacker News 和 reddit 上都評論過百的文章,順手譯出,enjoy:-) http://www.zhangzhibo.net/2014/02/01/

ios執行操作(十二)—— 自定義NSOperation實現網路下載後回撥

- (void)main { @autoreleasepool { // 下載圖片的耗時操作 NSURL *url = [NSURL URLWithString:self.urlString]; NSData *data = [NSDa

按鍵精靈實現大漠執行操作記事本

RunApp "regsvr32 c:\dm.dll /s" Dimenv hwnd,index:index=0 hwnd = Plugin.Window.SearchEx("Notepad", 0, 0) For i = 0 To UBound(split(hwnd,"|"

[Qt學習篇]Qthread實現執行操作

一、QThread類概述                                        QThread類為使用者管理多執行緒提供了一種平臺無關的途徑。 #include <QThread> 繼承自QObject類 二、QThread

【精】【執行】ListenableFuture非同步執行查詢實現

  業務場景:為優化查詢效率,將原有查詢的條件做成單獨的索引表,每次產生記錄就會同步到索引表中,每次查詢索引表,根據索引便利的條件欄位再分別查詢每張子表的內容,最後封裝成前臺要的實體類。這裡面涉及到非同步查詢,如何保證一條記錄下的子表全部都查出來後才執行下面的操作。 下面Demo簡

如何實現執行實現執行為什麼要調start,而不是run方法?(繼承Thread類、實現Ruable介面、Callable<V>)

什麼是程序? 作業系統中一個程式的執行週期(從開啟到關閉)。程序是具有一個或多個執行緒的執行緒組。 什麼是執行緒? 一個程序可以同時執行多個任務,任務就是執行緒,一個程序至少有一個執行緒。 執行緒執行在程序內部,執行緒是輕量級程序。 程序和執行緒比較:

執行之間實現同步

為什麼有執行緒安全問題   當多個執行緒同時共享同一個全域性變數或靜態變數,做寫的操作時,可能會發生資料衝突問題,也就是執行緒安全問題。但是做讀操作是不會發生資料衝突問題。 什麼是多執行緒之間同步   當多個執行緒共享同一個資源,不會受到其他執行緒的干擾。 需求說明:2個視窗同時賣100張火車票問題

執行 -- 執行實現

多執行緒----程序與執行緒 概念及二者區別與聯絡 多執行緒的實現 繼承Thread類實現多執行緒 實現Runnable介面來實現多執行緒 Thread與Runnable關係

c#執行操作測試(阻塞執行,結束任務)

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Te

執行模擬實現生產者/消費者模型

多執行緒模擬實現生產者/消費者模型 package com.chow.queue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java

Java8 並行流執行操作

並行流是一個把內容分成多個數據塊的,並用不同的執行緒分別處理每個資料塊的流。下面通過簡單的示例介紹一下順序流和並行流的特點。後面後時間在詳細記錄。 並行流: public static void main(String[] args) { List<

java執行操作兩個資料庫.

package com.dinglin; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; i

執行實現方法

Java提供了三種實現同步機制的方法: (1)synchronized 關鍵字 Java語言中,每個物件都有一個物件鎖與之關聯,該鎖表明物件在任何時候只允許被一個執行緒所擁有,當一個執行緒呼叫一段synchronized程式碼時,需要先獲取這個鎖,然後去執行相應的程式碼,

執行實現方式

多執行緒是指 一個程式執行時,產生或使用了不止一個執行緒。 執行緒的生命週期是怎麼樣的,下面這張圖我們可以看出些端倪: 這章我們主要討論多執行緒實現的方式,基礎知識部分我們可以下來再惡補。 方法一:Runnable 介面實現: 繼承介面,實現業務邏輯: public class WorkRunn