Lucene存儲結構
Lucene的索引結構是有層次結構的,主要分爲以下幾個層次:
- 索引(Index)
一個目錄一個索引,在Lucene中一個索引是放在一個文件夾中的。同一個文件夾中的文件構成一個Lucene索引
- 段(Segment)
一個索引包含多個段,段與段之間是獨立的,添加新文檔可以生成新的段,不同的段可以合併。在建立索引的時候對性能影響最大的地方就是在向索引寫入文檔的時候,所以在具體應用的時候就需要對此加以控制,段就是實現這種控制的。Lucene中默認每加入10個文檔就從內存往index文件寫入並生成一個段。
屬性 | 默認值 | 描述 |
---|---|---|
MergeFactory | 10 | 控制segment合併的頻率和大小 |
MaxMergeDocs | Int.MaxValue | 限制每個segment中包含的文檔數 |
MinMergeDocs | 10 | 當內存中的文檔達到多少的時候再寫入segment |
- 文檔(Document)
文檔是我們建索引的基本單位,一個段可以包含多篇文檔。新添加的文檔是單獨保存在一個新生成的段中,隨着段的合併,不同的文檔合併到同一個段中.
- 域(Field)
一篇文檔包含不同類型的信息,可以分開索引,比如標題、內容、作者等,可以保存在不同的域中
- 詞(Term)
詞是索引的最小單位,是經過詞法分析和語言處理後的字符串。
爲何Lucene大數據量搜索快, 要分兩部分來看:
- 因爲底層的倒排索引存儲結構
- 因爲詞典的索引結構,查詢關鍵字的時候速度快
Lucene的索引結構中,即保存了正向信息,也保存了反向信息。
- 正向信息
按層次保存了從索引一直到詞的包含關係:索引(Index) –> 段(segment) –> 文檔(Document)–> 域(Field) –> 詞(Term)
- 反向信息
保存了詞典到倒排表的映射:詞(Term) –> 文檔(Document)
字典的數據結構對比:
數據結構 | 說明 |
---|---|
排序列表Array/List | 使用二分法查找,不平衡 |
HashMap/TreeMap | 性能高,內存消耗大,幾乎是原始數據的三倍 |
Skip List | 跳躍表,可快速查找詞語,在lucene、redis、Hbase等均有實現。相對於TreeMap等結構,特別適合高併發場景 |
Trie | 適合英文詞典,如果系統中存在大量字符串且這些字符串基本沒有公共前綴,則相應的trie樹將非常消耗內存 |
Double Array Trie | 適合做中文詞典,內存佔用小,很多分詞工具均採用此種算法 |
Ternary Search Tree | 三叉樹,每一個node有3個節點,兼具省空間和查詢快的優點 |
Finite State Transducers (FST) | 一種有限狀態轉移機,Lucene 4有開源實現,並大量使用 |
TST的結構:
- 優點:內存佔用率低,壓縮率一般在3倍~20倍之間、模糊查詢支持好、查詢快
- 缺點:結構複雜、輸入要求有序、更新不易
Lucene優化
- 過濾請求
有些單詞不在索引庫裏,但還需要進索引庫查詢,發起不必要的IO請求。
使用布隆過濾器,預先判斷單詞是不是在該索引庫裏。布隆過濾器原理很簡單,對一單詞哈希,並映射到相應bit,設置爲1,判斷時同樣做哈希,並去相應bit位取值,若爲1,則可能存在,進庫查詢,若爲0,則肯定不存在,不需進庫查詢。
- 通過設置IndexWriter的參數優化
maxBufferedDocs、maxMergeDocs、mergeFactor
- 選擇合適的分詞器
不同的分詞器分詞效果不同,所用時間也不同。雖然StandardAnalyzer切分詞速度快過IKAnalyzer,但是由於StandardAnalyzer對中文支持不好,所以爲了追求好的分詞效果, 爲了追求查詢時的準確率,最好用IKAnalyzer分詞器,IKAnalyzer支持停用詞典和擴展詞典,可以通過調整兩個詞典中的內容,來提升查詢匹配的精度。
4. 屏蔽打分、排序機制(如果業務上不需要的話)
indexSearcher.setSimilarity(indexSearcher.getSimilarity(false));
- 選擇合適的對應存放索引庫
類 | 寫操作 | 讀操作 | 特點 |
---|---|---|---|
SimpleFSDirectory | RandomAccessFile | RandomAccessFile | 簡單實現、併發能力差 |
NIOFSDirectory | FileChannel | FSDirectory.FSIndexOutput | 併發能力強,但windows系統下有bug |
MMapDirectory | 內存映射 | FSDirectory.FSIndexOutput | 讀取操作基於內存 |
注意事項
-
關鍵詞區分大小寫
OR AND TO等關鍵詞是區分大小寫的,lucene只認大寫的,小寫的當做普通單詞。 -
讀寫互斥性
同一時刻只能有一個對索引的寫操作,在寫的同時可以進行搜索
- 文件鎖
寫索引的過程中強行退出將在tmp目錄留下一個lock文件,使以後的寫操作無法進行,可以將其手工刪除
- 時間格式
lucene只支持一種時間格式yyMMddHHmmss,所以你傳一個yy-MM-dd HH:mm:ss的時間給lucene,它是不會當作時間來處理的。
使用工具類可以把日期處理成yyMMdd
DateTools.dateToString(new Date(), DateTools.Resolution.DAY)
- 設置 boost
有些時候在搜索時某個字段的權重需要大一些,例如你可能認爲標題中出現關鍵詞的文章比正文中出現關鍵詞的文章更有價值,你可以把標題的boost設置的更大,那麼搜索結果會優先顯示標題中出現關鍵詞的文章.
Query termQuery = new TermQuery(new Term("name","lucene"));
BoostQuery query =new BoostQuery(termQuery, 3.5f);