2011-03-28 53 views
2


我的lucene索引經常用新記錄進行更新,我在索引中有5,000,000條記錄,並且正在使用FieldCache緩存我的一個數字字段。但在更新索引後,需要一段時間才能重新加載FieldCache(即時重新加載緩存原因文檔,表示DocID不可靠),那麼如何通過向FieldCache僅添加新添加的DocID來最大限度地減少此開銷,從而導致此功能變爲瓶頸應用。頻繁更新索引的FieldCache


IndexReader reader = IndexReader.Open(diskDir); 
int[] dateArr = FieldCache_Fields.DEFAULT.GetInts(reader, "newsdate"); // This line takes 4 seconds to load the array 
dateArr = FieldCache_Fields.DEFAULT.GetInts(reader, "newsdate"); // this line takes 0 second as we expected 
// HERE we add some document to index and we need to reload the index to reflect changes 

reader = reader.Reopen(); 
dateArr = FieldCache_Fields.DEFAULT.GetInts(reader, "newsdate"); // This takes 4 second again to load the array 

我希望有一個機制,通過增加僅對新增文件到我們的數組中的索引減少這個時候有這樣http://invertedindex.blogspot.com/2009/04/lucene-dociduid-mapping-and-payload.html 的技術來提高性能,但它仍然加載,我們已經把所有的文件和我認爲如果我們找到一種方法只是將新添加的文檔添加到陣列中,則無需重新加載它們全部

+0

你的代碼的問題是我用內部/外部閱讀器描述的。您將外部閱讀器(DirectoryReader)傳遞給FieldCache。它認爲這兩個讀者是不同的,並分別緩存它們。您需要使用最內層的閱讀器,即段閱讀器來爲每個段填充它。這意味着它只會在你打電話給Reopen之後加載更改。我會在幾分鐘後爲此發佈一些代碼。 – sisve 2011-04-03 06:50:35

回答

4

FieldCache使用弱引用來將索引讀取器用作緩存的關鍵字。 (通過呼叫IndexReader.GetCacheKey已被廢除)。對IndexReader.Open的標準呼叫FSDirectory將使用一個閱讀器池,每個閱讀器一個閱讀器。

您應該始終將最內層的閱讀器傳遞給FieldCache。查看ReaderUtil以獲取某個幫助程序的內容以檢索包含文檔的個人閱讀器。文檔ID不會在段中更改,將其描述爲不可預知/易失性時,它的含義是它將在兩個索引提交之間更改。已刪除的文件可能已被引用,段已合併,以及此類操作。

提交需要從磁盤中刪除該段(合併/優化),這意味着新的閱讀器不會有合併的段閱讀器,並且只要所有較舊的閱讀器關閉,垃圾收集器就會將其刪除。

永遠不要致電FieldCache.PurgeAllCaches()。它意味着測試,而不是生產使用。

添加2011-04-03;使用子閱讀器的示例代碼。

var directory = FSDirectory.Open(new DirectoryInfo("index")); 
var reader = IndexReader.Open(directory, readOnly: true); 
var documentId = 1337; 

// Grab all subreaders. 
var subReaders = new List<IndexReader>(); 
ReaderUtil.GatherSubReaders(subReaders, reader); 

// Loop through all subreaders. While subReaderId is higher than the 
// maximum document id in the subreader, go to next. 
var subReaderId = documentId; 
var subReader = subReaders.First(sub => { 
    if (sub.MaxDoc() < subReaderId) { 
     subReaderId -= sub.MaxDoc(); 
     return false; 
    } 

    return true; 
}); 

var values = FieldCache_Fields.DEFAULT.GetInts(subReader, "newsdate"); 
var value = values[subReaderId]; 
+0

謝謝西蒙,但我想確保如果我添加新文檔到我的索引該文檔的文檔ID將永遠不會改變合併或優化原因,如果它改變上述解決方案不符合我的需要,因爲我想只向FieldCache提供新添加的文檔,以防止再次使用FieldCache加載所有文檔,如果我可以確保哪些分段閱讀器在合併/優化期間保持完好,那麼我可以根據您的解決方案和重新加載其他分段讀取器的價值,它會逐步提高性能,但它仍然不是理想的,因爲我想 – Ehsan 2011-04-01 05:56:14

+0

儘管技術上合併/優化後讀取器完好無損,但它們也被廢棄並用新創建的分段替換。你能否提供一些在當前設置中遇到問題的代碼示例? – sisve 2011-04-01 13:47:06

+0

謝謝你的代碼是完美的西蒙 – Ehsan 2011-04-03 19:25:36

1

以下是我解決此問題的一種方法。您需要創建一個後臺線程來構造IndexSearcher實例,每隔一段時間一次。繼續使用當前的IndexSearcher實例,直到後臺線程中的新線程準備就緒。然後換出新的是你現在的那個。每個實例都充當索引從第一次打開時的快照。請注意,FieldCache的內存開銷增加了一倍,因爲您一次需要在內存中創建兩個實例。在發生這種情況時,您可以放心地寫信至IndexWriter

如果您需要,您可以通過立即進行索引更改以進行搜索來進一步探索,儘管它可能會變得棘手。您需要將RAMDirectory與上面的每個快照實例相關聯,以將更改保留在內存中。然後創建第二個IndexWriter指向那個RAMDirectory。對於每個索引編寫,您需要寫入兩個IndexWriter實例。對於搜索,您將在RAMDirectory之間使用MultiSearcher,並在磁盤上使用正常索引。一旦IndexSearcher不再使用,RAMDirectory可以扔掉。我在這裏詳述了一些細節,但是這是一般的想法。

希望這會有所幫助。

+0

假設你在磁盤上的FSDirectory中有1000條記錄並使用FieldCache加載它,並且你在RAMDirectory中有新的10條記錄,就像你上面解釋的那樣,所以我們有兩個ID爲0,...,10的文檔,因爲每個目錄都有它自己的docID我不能創建一個具有唯一docID的集成FieldCache,並且在添加記錄10次之後,我還優化了我的索引。在這種情況下,docID可能會改變。 – Ehsan 2011-03-28 09:08:32

+0

第二部分的技巧是,你將在'FSDirectory'和'RAMDirectory'之間使用'MultiSearcher',這樣''''''''''''''''在'RAMDirectory'開始變化之前打開'FSDirectory'。所以它只是看起來存在給定ID的兩個文件中的一個。當您執行搜索時,'MultiSearcher'處理合並這兩者。除非你在搜索之外使用FieldCache?不過,我會從第一部分開始,在後臺打開第二個'IndexSearcher'(或'IndexReader')實例,讓它構建FieldCache,然後將其交換出去。 – WhiteFang34 2011-03-28 09:20:15

+0

是的,我想在CustomScoreQuery搜索外使用FieldCache – Ehsan 2011-03-28 11:53:25