2011-08-09 14 views
0

我想構建一個Lucene序列化類,該序列化將用DataMember裝飾的屬性序列化/反序列化對象(類),並指定如何存儲屬性的特殊屬性/ Lucene索引中的字段。在C#中的Lucene串行器,需要性能建議

當我需要通過某個鍵/值對檢索單個對象時,該類正常工作。 但我注意到,如果有時我需要檢索所有項目,那麼我們假設有100,000個文檔 - 然後MySQL做到這一點〜大約快10倍...由於某種原因...

您能否檢查此代碼(Lucene專家),並提出任何可能的性能改進相關的想法?

public IEnumerable<T> LoadAll() 
{ 
    IndexReader reader = IndexReader.Open(this.PathToLuceneIndex); 
    int itemsCount = reader.NumDocs(); 

    for (int i = 0; i < itemsCount; i++) 
    { 
     if (!reader.IsDeleted(i)) 
     { 
      Document doc = reader.Document(i); 

      if (doc != null) 
      { 
       T item = Deserialize(doc); 
       yield return item; 
      } 
     } 
    } 

    if (reader != null) reader.Close(); 
} 

private T Deserialize(Document doc) 
{ 
    T itemInstance = Activator.CreateInstance<T>(); 

    foreach (string fieldName in fieldTypes.Keys) 
    { 
     Field myField = doc.GetField(fieldName); 

     //Not every document may have the full collection of indexable fields 
     if (myField != null) 
     { 
      object fieldValue = myField.StringValue(); 
      Type fieldType = fieldTypes[fieldName]; 

      if (fieldType == typeof(bool)) 
       fieldValue = fieldValue == "1" ? true : false; 

      if (fieldType == typeof(DateTime)) 
       fieldValue = DateTools.StringToDate((string)fieldValue); 

      pF.SetValue(itemInstance, fieldName, fieldValue); 
     } 
    } 

    return itemInstance; 
} 

預先感謝您!

回答

1

這裏有一些提示:

首先,不要使用IndexReader.Open(string path)。它不僅會在Lucene.net的下一個主要版本中被刪除,它通常不是您最好的選擇。當您讓Lucene爲您生成目錄時,實際上會有大量不必要的代碼。我建議:

var dir = new SimpleFSDirectory(new DirectoryInfo(path)); 
var reader = IndexReader.Open(dir, true); 

你也應該這樣做,因爲我上面那樣,打開IndexReader爲只讀,如果你不絕對需要寫它,因爲它會在多線程環境中尤其快。

如果您知道索引的大小不超過您可以容納的內存(即小於500-600 MB而未壓縮),則可以使用RAMDirectory代替。這會將整個索引加載到內存中,如果您將索引留在磁盤上,則可以繞過大部分昂貴的IO操作。它應該極大提高你的速度,特別是如果你做它與下面的其他建議。

如果索引太大而不適合內存,則需要將索引拆分爲塊(即每nMB的索引),或者繼續從磁盤讀取索引。

另外,我知道你不能yield returntry...catch,但你可以在try...finally,我會建議你包裹邏輯LoadAll()try...finally,像

IndexReader reader = null; 
try 
{ 
    //logic here... 
} 
finally 
{ 
    if (reader != null) reader.Close(); 
} 

現在,當談到到你實際的Deserialize代碼,你可能會以幾乎最快的方式做到這一點,除非你不需要的時候裝上了字符串。 Lucene只將字段存儲爲byte []數組或字符串。由於您調用的是字符串值,因此您知道它始終是一個字符串,並且只有在絕對必要時才必須將其裝箱。將其更改爲:

string fieldValue = myField.StringValue(); 

這將至少有時爲您節省一小筆拳擊費用。 (真的,不是很多)

關於拳擊的話題,我們正在研究lucene的一個分支,你可以從SVN中提取,它改變了Lucene內部使用裝箱容器(ArrayLists,非泛型列表和HashTables )到一個使用泛型和更多.net友好的東西的版本。這是2.9.4g branch。正如我們想說的,網絡化。我們還沒有正式對它進行基準測試,但是開發者測試顯示它在某些情況下比舊版本要快200%左右。

另一件需要記住的事情是,Lucene作爲一個搜索引擎非常棒,你可能會發現在某些情況下,它可能不能堆棧到MySQL。實際上,確實知道的唯一方法就是測試並嘗試找到像上面提到的那些性能瓶頸。

希望有幫助!如果您有任何問題,請不要忘記Lucene.Net郵件列表([email protected])。我和其他提交者通常很快回答問題。

+0

謝謝您的意見。我已經按照建議實施了RAMDirectory,而讀者只讀爲...拳擊費用並不顯着,我會嘗試通過以某種方式調整Activator.CreateInstance ();並找到用反射設置屬性值的最快方法。 – Denis

+0

有沒有比Document doc = reader.Document(i)更快速獲取/閱讀文檔的方法; ? - 因爲,例如對於我的文件集 - 大約需要700毫秒。檢索100,000個項目......這是在創建對象實例(新建)之前,將DateTime和布爾值(從字符串轉換爲..),使用反射設置值等等。 – Denis

+1

這是獲取對象的首選方法文件。獲取文檔的一部分問題是,當您加載文檔時,這些字段會加載到內存中。國際海事組織,Lucene應該總是懶洋洋地加載文件字段,但是當前版本的lucene不這樣做。您可以使用MapFieldSelector並只選擇您需要的字段或將字典傳遞給字符串FieldSelectorResult,並且您可以將其告知LazyLoad某些字段。你將不得不加載你最終需要的數據,所以它只是推遲了成本。 –