Lucene.net 系列三 --- index 中

本文將進一步討論有關Lucene.net建立索引的問題:
主要包含以下主題:
1.索引的權重
2.利用IndexWriter 屬性對建立索引進行高級管理
3.利用RAMDirectory充分發揮內存的優勢
4.利用RAMDirectory並行建立索引
5.控制索引內容的長度
6.Optimize 優化的是什麼?
本文將進一步討論有關Lucene.net建立索引的問題:
索引的權重
根據文檔的重要性的不同,顯然對於某些文檔你希望提高權重以便將來搜索的時候,更符合你想要的結果. 下面的代碼演示瞭如何提高符合某些條件的文檔的權重.
比如對公司內很多的郵件做了索引,你當然希望主要查看和公司有關的郵件,而不是員工的個人郵件.這點根據郵件的地址就可以做出判斷比如包含@alphatom.com的就是公司郵件,而@gmail.com等等就是私人郵件.如何提高相應郵件的權重? 代碼如下:
     public static String COMPANY_DOMAIN = "alphatom.com";
     Document doc = new Document();
     String senderEmail = GetSenderEmail();
     String senderName = getSenderName();
     String subject = GetSubject();
     String body = GetBody();
     doc.Add(Field.Keyword("senderEmail”
, senderEmail));
     doc.Add(Field.Text("senderName", senderName));
     doc.Add(Field.Text("subject", subject));
     doc.Add(Field.UnStored("body", body));
     if (GetSenderDomain().EndsWith(COMPANY_DOMAIN))
     //如果是公司郵件,提高權重,默認權重是1.0
           doc.SetBoost(1.5);                      
     else                          //如果是私人郵件,降低權重
.
           doc.SetBoost(0.1);
     writer.AddDocument(doc);
不僅如此你還可以對Field也設置權重.比如你對郵件的主題更感興趣.就可以提高它的權重.   
    Field senderNameField = Field.Text("senderName", senderName);
     Field subjectField = Field.Text("subject", subject);
     subjectField.SetBoost(1.2);
lucene搜索的時候會對符合條件的文檔按匹配的程度打分,這點就和google的PageRank有點類似, 而SetBoost中的Boost就是其中的一個因素,當然還有其他的因素.這要放到搜索裏再說.
利用IndexWriter 變量對建立索引進行高級管理
在建立索引的時候對性能影響最大的地方就是在將索引寫入文件的時候, 所以在具體應用的時候就需要對此加以控制.
在建立索引的時候對性能影響最大的地方就是在將索引寫入文件的時候所以在具體應用的時候就需要對此加以控制
IndexWriter屬性   默認值 描述
MergeFactory 10 控制segment合併的頻率和大小
MaxMergeDocs Int32.MaxValue 限制每個segment中包含的文檔數
MinMergeDocs 10 當內存中的文檔達到多少的時候再寫入segment
Lucene默認情況是每加入10份文檔就從內存往index文件寫入並生成一個segement,然後每10個segment就合併成一個segment.通過MergeFactory這個變量就可以對此進行控制.
MaxMergeDocs用於控制一個segment文件中最多包含的Document數.比如限制爲100的話,即使當前有10個segment也不會合並,因爲合併後的segmnet將包含1000個文檔,超過了限制.
MinMergeDocs用於確定一個當內存中文檔達到多少的時候才寫入文件,該項對segment的數量和大小不會有什麼影響,它僅僅影響內存的使用,進一步影響寫索引的效率.
爲了生動的體現這些變量對性能的影響,用一個小程序對此做了說明.
這裏有點不可思議.Lucene in Action書上的結果比我用dotLucene做的結果快了近千倍.這裏給出書中用Lucene的數據,希望大家比較一下看看是不是我的問題.
Lucene in Action書中的數據:
% java lia.indexing.IndexTuningDemo 100000 10 9999999 10
Merge factor: 10
Max merge docs: 9999999
Min merge docs: 10
Time: 74136 ms
% java lia.indexing.IndexTuningDemo 100000 100 9999999 10
Merge factor: 100
Max merge docs: 9999999
Min merge docs: 10
Time: 68307 ms

我的數據: 336684128 ms
可以看出MinMergeDocs(主要用於控制內存)和MergeFactory(控制合併的次數和合並後的大小) 對建立索引有顯著的影響.但是並不是MergeFactory越大越好,因爲如果一個segment的文檔數很多的話,在搜索的時候必然也會影響效率,所以這裏MergeFactory的取值是一個需要平衡的問題.而MinMergeDocs主要受限於內存.
利用RAMDirectory充分發揮內存的優勢
從上面來看充分利用內存的空間,減少讀寫文件(寫入index)的次數是優化建立索引的重要方法.其實在Lucene中提供了更強大的方法來利用內存建立索引.使用RAMDirectory來替代FSDirectory. 這時所有的索引都將建立在內存當中,這種方法對於數據量小的搜索業務很有幫助,同時可以使用它來進行一些小的測試,避免在測試時頻繁建立刪除索引文件.
在實際應用中RAMDirectory和FSDirectory協作可以更好的利用內存來優化建立索引的時間.
具體方法如下:
1.建立一個使用FSDirectory的IndexWriter
2 .建立一個使用RAMDirectory的IndexWriter
3 把Document添加到RAMDirectory中
4 當達到某種條件將RAMDirectory 中的Document寫入FSDirectory.
5 重複第三步
示意代碼:
     private FSDirectory fsDir = FSDirectory.GetDirectory("index",true);
    private RAMDirectory ramDir = new RAMDirectory();
       private IndexWriter fsWriter = IndexWriter(fsDir,new SimpleAnalyzer(), true);
       private IndexWriter ramWriter = new IndexWriter(ramDir,new SimpleAnalyzer(), true);
       while (there are documents to index)
      {
         ramWriter.addDocument(doc);
         if (condition for flushing memory to disk has been met)
         {
           fsWriter.AddIndexes(Directory[]{ramDir}) ;
           ramWriter.Close();          //why not support flush?
           ramWriter =new IndexWriter(ramDir,new SimpleAnalyzer(),true);
         }
     }
這裏的條件完全由用戶控制,而不是FSDirectory採用對Document計數的方式控制何時寫入文件.相比之下有更大的自由性,更能提升性能.
利用RAMDirectory並行建立索引
RAMDirectory還提供了使用多線程來建立索引的可能性.下面這副圖很好的說明了這一點.  
甚至你可以在一個高速的網絡裏使用多臺計算機來同時建立索引.就像下面這種圖所示.
雖然有關並行同步的問題需要你自己進行處理,不過通過這種方式可以大大提高對大量數據建立索引的能力.
控制索引內容的長度.
在我的一篇速遞介紹過Google Desktop Search只能搜索到文本中第5000個字的.也就是google在建立索引的時候只考慮前5000個字,在Lucene中同樣也有這個配置功能.
Lucene對一份文本建立索引時默認的索引長度是10,000. 你可以通過IndexWriter 的MaxFieldLength屬性對此加以修改.還是用一個例子說明問題.
     [Test]
     public void FieldSize()      
     // AddDocuments 和 GetHitCount都是自定義的方法,詳見源代碼
     {
         AddDocuments(dir, 10);      
         //第一個參數是目錄,第二個配置是索引的長度
         Assert.AreEqual(1, GetHitCount("contents", "bridges"))
         //原文檔的contents爲”Amsterdam has lots of bridges”
         //當索引長度爲10個字時能找到bridge
         AddDocuments(dir, 1);
         Assert.AreEqual(0, GetHitCount("contents", "bridges"));
         //當索引長度限制爲1個字時就無法發現第5個字bridges
     }
    對索引內容限長往往是處於效率和空間大小的考慮.能夠對此進行配置是建立索引必備的一個功能.
Optimize 優化的是什麼?
在以前的例子裏,你可能已經多次見過writer.Optimize()這段代碼.Optimize到底做了什麼?
讓你吃驚的是這裏的優化對於建立索引不僅沒有起到加速的作用,反而是延長了建立索引的時間.爲什麼?
因爲這裏的優化不是爲建立索引做的,而是爲搜索做的.之前我們提到Lucene默認每遇到10個Segment就合併一次,儘管如此在索引完成後仍然會留下幾個segmnets,比如6,7.
而Optimize的過程就是要減少剩下的Segment的數量,儘量讓它們處於一個文件中.
它的過程很簡單,就是新建一個空的Segmnet,然後把原來的幾個segmnet全合併到這一個segmnet中,在此過程中,你的硬盤空間會變大,因爲同時存在兩份一樣大小的索引.不過在優化完成後,Lucene會自動將原來的多份Segments刪除,只保留最後生成的一份包含原來所有索引的segment.
儘量減少segments的個數主要是爲了增加查詢的效率.假設你有一個Server,同時有很多的Client建立了各自不同的索引,如果此時搜索,那麼必然要同時打開很多的索引文件,這樣顯然會受到很大的限制,對性能產生影響.
當然也不是隨時做Optimize就好,如前所述做優化時要花費更多的時間和空間,而且在做優化的時候是不能進行查詢的.所以索引建立的後期,並且索引的內容不會再發生太多的變化的時候做優化是一個比較好的時段.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章