lucene3.0_和IndexWriter有關的幾個引數設定及重建索引注意事項

weixin_34262482發表於2010-10-16

系列彙總:

lucene3.0_基礎使用及注意事項彙總

 

part1:

本文介紹和IndexWriter有關的3個引數:

1.MAXBufferedDocs

 MaxBufferedDocs這個引數預設是disabled的,因為Lucene中還用另外一個引數(RAMBufferSizeMB)控制這個bufffer的索引文件個數。
其實MaxBufferedDocs和RAMBufferSizeMB這兩個引數是可以一起使用的,一起使用時只要有一個觸發條件滿足就寫入硬碟,生成一個新的索引segment檔案。

2.RAMBufferSize

 控制用於buffer索引文件的記憶體上限,如果buffer的索引文件個數到達該上限就寫入硬碟。當然,一般來說也只越大索引速度越快。當我們對文件大小不太確定時,這個引數就相當有用,不至於outofmemory error.

3.MegerFactor

SetMergeFactor是控制segment合併頻率的,其決定了一個索引塊中包括多少個文件,當硬碟上的索引塊達到多少時,將它們合併成一個較大的索引塊。當MergeFactor值較大時,生成索引的速度較快。MergeFactor的預設值是10,建議在建立索引前將其設定的大一些。

 

part2:

注意:

1.不要隨意設定MaxbufferedDocs。

MaxBufferedDocs和RAMBufferSize共同控制記憶體中文件的容量。

如果對MaxBufferedDocs進行設定要比較小心了,因為它本身是disabled,如果設定不合理將導致大規模的重建索引非常慢。

例如:

 

dir = SimpleFSDirectory.open(new File("d:/20101015index"));
writer
=new IndexWriter(dir, new StandardAnalyzer(Version.LUCENE_30), true, MaxFieldLength.UNLIMITED);
writer.setUseCompoundFile(
true);
writer.setMaxBufferedDocs(
100);
writer.setMergeFactor(
10);
//add document
//注意點2:filed例項在多次新增的時候可以重用,節約構造field例項的時間。
Field f1 =new Field("f1", "", Store.YES, Index.ANALYZED) ;
Field f2
=new Field("f2", "", Store.YES, Index.ANALYZED) ;
for (int i =0; i <1000000; i++) {
Document doc
=new Document();
f1.setValue(
"f1 hello doc"+ i);
doc.add(f1);
f2.setValue(
"f2 world doc"+ i);
doc.add(f2);
writer.addDocument(doc);
}
writer.optimize();

 

上述程式碼中對MaxBufferedDocs進行設定:

 

writer.setMaxBufferedDocs(100);

那麼現在對記憶體中文件的容量有兩個控制量:

1.文件數量達到100就寫回磁碟;

2.文件總容量達到16m就寫回磁碟;

因為上面一個文件的大小很小,100個文件的容量肯定不會達到16m,所以第一個控制量起到主要的作用。

在沒有設定MaxBufferedDocs之前就只有RAMBufferSize(16m)控制記憶體中文件的數量,

較之這兩種情況,明顯是沒有設定MaxbufferedDocs更好的利用了記憶體,因此建立索引更快速。

實驗證明:

不設定MaxbufferedDocs,耗時20s左右;

設定MaxbufferedDocs為100,耗時280000s左右。 = =! 差別太大了,所以一定要小心!!!

 

2.不要太迷信MegerFactor比較大會加快重建索引的速度。

通過實驗,在上述程式碼中獎MegerFactor設定成10比100要快2s。

 

“一家之言”——

對於一般的系統(資料量在千萬級,重建索引暫不考慮併發壓力),在重建索引時不要太糾結這些引數的設定,

只要不犯太嚴重的問題(例如像上面一樣將MaxbufferedDocs設定得過小,還不如不設定),效率出入都不會太大。

 

3.是否有必要將資料先放到RAM中,再和磁碟的索引進行合併?

我認為在重建索引的環節沒有必要。因為在使用FSDirectory建立索引的時候不就可以控制記憶體的使用麼!?(MaxbufferedDocs和RAMBufferSize)

而RAMDiretory應該重點使用在實時索引上面。

(= =! 我指的重建索引是什麼意思?"對大量的資料一次性建立成磁碟索引!")

這裡也做了一個測試,是先將文件寫入記憶體,然後合併到磁碟。

主要程式碼如下所示:

 

dir =new RAMDirectory();
dirFS
= SimpleFSDirectory.open(new File("d:/20101015index"));
writer
=new IndexWriter(dir, new StandardAnalyzer(Version.LUCENE_30), MaxFieldLength.UNLIMITED);
writerFS
=new IndexWriter(dirFS, new StandardAnalyzer(Version.LUCENE_30), true, MaxFieldLength.UNLIMITED);
//
Field f1 =new Field("f1", "", Store.YES, Index.ANALYZED);
Field f2
=new Field("f2", "", Store.YES, Index.ANALYZED);
for (int i =0; i <1000000; i++) {
Document doc
=new Document();
f1.setValue(
"f1 hello doc"+ i);
doc.add(f1);
f2.setValue(
"f2 world doc"+ i);
doc.add(f2);
writer.addDocument(doc);
}
// writer.commit();
writerFS.addIndexes(writer.getReader());

 

經過測試,發現效率和直接寫磁碟差不多!

 

剛開始學lucene的時候,總熱衷提高效率,什麼引數的設定啊、RAM的使用啊等等,殊不知重建索引是一個初始化階段(服務於後期的檢索),

就算有優化的必要,但絕不是當前必要解決的“主要矛盾”。

應該將重點放到:

1.實時索引;

2.分散式構建檢索框架(如果系統規模有必要的);

3.怎麼在程式質量上利用好記憶體,不至於執行時到處時 記憶體溢位 。

 

part3:

轉載了一篇相關文章——

影響Lucene索引速度原因以及提高索引速度技巧

• 確認你在使用最新的Lucene版本。

• 儘量使用本地檔案系統

遠端檔案系統一般來說都會降低索引速度。如果索引必須分佈在遠端伺服器,請嘗試先在本地生成索引,然後分發到遠端伺服器上。
• 使用更快的硬體裝置,特別是更快的IO裝置

• 在索引期間複用單一的IndexWriter例項

• 使用按照記憶體消耗Flush代替根據文件數量Flush 
在Lucene 2.2之前的版本,可以在每次新增文件後呼叫ramSizeInBytes方法,當索引消耗過多的記憶體時,然後在呼叫flush()方法。這樣做在索引大量小文件或者文件大小不定的情況下尤為有效。你必須先把maxBufferedDocs引數設定足夠大,以防止writer基於文件數量flush。但是注意,別把這個值設定的太大,否則你將遭遇Lucene-845號BUG。不過這個BUG已經在2.3版本中得到解決。

在Lucene2.3之後的版本。IndexWriter可以自動的根據記憶體消耗呼叫flush()。你可以通過writer.setRAMBufferSizeMB()來設定快取大小。當你打算按照記憶體大小flush後,確保沒有在別的地方設定MaxBufferedDocs值。否則flush條件將變的不確定(誰先符合條件就按照誰)。

• 在你能承受的範圍內使用更多的記憶體 
在flush前使用更多的記憶體意味著Lucene將在索引時生成更大的segment,也意味著合併次數也隨之減少。在Lucene-843中測試,大概48MB記憶體可能是一個比較合適的值。但是,你的程式可能會是另外一個值。這跟不同的機器也有一定的關係,請自己多加測試,選擇一個權衡值。

• 關閉複合檔案格式
呼叫setUseCompoundFile(false)可以關閉複合檔案選項。生成複合檔案將消耗更多的時間(經過Lucene-888測試,大概會增加7%-33%的時間)。但是請注意,這樣做將大大的增加搜尋和索引使用的檔案控制程式碼的數量。如果合併因子也很大的話,你可能會出現用光檔案控制程式碼的情況。

• 重用Document和Field例項 
在lucene 2.3中,新增了一個叫setValue的方法,可以允許你改變欄位的值。這樣的好處是你可以在整個索引程式中複用一個Filed例項。這將極大的減少GC負擔。

最好建立一個單一的Document例項,然後新增你想要的欄位到文件中。同時複用新增到文件的Field例項,通用呼叫相應的SetValue方法改變相應的欄位的值。然後重新將Document新增到索引中。

注意:你不能在一個文件中多個欄位共用一個Field例項,在文件新增到索引之前,Field的值都不應該改變。也就是說如果你有3個欄位,你必須建立3個Field例項,然後再之後的Document新增過程中複用它們。

• 在你的分析器Analyzer中使用一個單一的Token例項 
在分析器中共享一個單一的token例項也將緩解GC的壓力。

• 在Token中使用char[]介面來代替String介面來表示資料 
在Lucene 2.3中,Token可以使用char陣列來表示他的資料。這樣可以避免構建字串以及GC回收字串的消耗。通過配合使用單一Token例項和使用char[]介面你可以避免建立新的物件。

• 設定autoCommit為false
在Lucene 2.3中對擁有儲存欄位和Term向量的文件進行了大量的優化,以節省大索引合併的時間。你可以將單一複用的IndexWriter例項的autoCommit設定為false來見證這些優化帶來的好處。注意這樣做將導致searcher在IndexWriter關閉之前不會看到任何索引的更新。如果你認為這個對你很重要,你可以繼續將autoCommit設定為true,或者週期性的開啟和關閉你的writer。

• 如果你要索引很多小文字欄位,如果沒有特別需求,建議你將這些小文字欄位合併為一個大的contents欄位,然後只索引contents。(當然你也可以繼續儲存那些欄位)

• 加大mergeFactor合併因子,但不是越大越好 
大的合併因子將延遲segment的合併時間,這樣做可以提高索引速度,因為合併是索引很耗時的一個部分。但是,這樣做將降低你的搜尋速度。同時,你有可能會用光你的檔案控制程式碼如果你把合併因子設定的太大。值太大了設定可能降低索引速度,因為這意味著將同時合併更多的segment,將大大的增加硬碟的負擔。

• 關閉所有你實際上沒有使用的功能 
如果你儲存了欄位,但是在查詢時根本沒有用到它們,那麼別儲存它們。同樣Term向量也是如此。如果你索引很多的欄位,關閉這些欄位的不必要的特性將對索引速度提升產生很大的幫助。

• 使用一個更快的分析器
有時間分析文件將消耗很長的時間。舉例來說,StandardAnalyzer就比較耗時,尤其在Lucene 2.3版本之前。你可以嘗試使用一個更簡單更快但是符合你需求的分析器。

• 加速文件的構建時間 
在通常的情況下,文件的資料來源可能是外部(比如資料庫,檔案系統,蜘蛛從網站上的抓取等),這些通常都比較耗時,儘量優化獲取它們的效能。

• 在你真的需要之前不要隨意的優化optimize索引(只有在需要更快的搜尋速度的時候)

• 在多執行緒中共享一個IndexWriter
最新的硬體都是適合高併發的(多核CPU,多通道記憶體構架等),所以使用多執行緒新增文件將會帶來不小的效能提升。就算是一臺很老的機器,併發新增文件都將更好的利用IO和CPU。多測試併發的執行緒數目,獲得一個臨界最優值。

• 將文件分組在不同的機器上索引然後再合併
如果你有大量的文字文件需要索引,你可以把你的文件分為若干組,在若干臺機器上分別索引不同的組,然後利用writer.addIndexesNoOptimize來將它們合併到最終的一個索引檔案中。

• 執行效能測試程式
如果以上的建議都沒有發生效果。建議你執行下效能檢測程式。找出你的程式中哪個部分比較耗時。這通常會給你想不到的驚喜。


本翻譯屬於原創,轉載時請註明出處,英文原版請檢視:

http://wiki.apache.org/jakarta-lucene/ImproveIndexingSpeed


轉自:

http://blog.csdn.net/gaoweipeng/archive/2009/10/16/4684198.aspx

 

 

相關文章