lucene DocValues——沒有看懂


前言:
在Lucene4.x之后,出現一個重大的特性,就是索引支持DocValues,這對於廣大的solr和elasticsearch用戶,無疑來說是一個福音,這玩意的出現通過犧牲一定的磁盤空間帶來的好處主要有兩個:
(1)節省內存
(2)對排序,分組和一些聚合操作時能夠大大提升性能

下面來詳細介紹下DocValue的原理和使用場景

(一)什么是DocValues?

DocValues其實是Lucene在構建索引時,會額外建立一個有序的基於document => field value的映射列表;

(二)為什么要用DocValues ?

基於lucene的solr和es都是使用經典的倒排索引模式來達到快速檢索的目的,簡單的說就是建立 搜索詞=》 文檔id列表 這樣的關系映射,
然后在搜索時,通過類似hash算法,來快速定位到一個搜索關鍵詞,然后讀取其的文檔id集合,這就是倒排索引的核心思想,這樣搜索數據
是非常高效快速的,當然它也是有缺陷的,假如我們需要對數據做一些聚合操作,比如排序,分組時,lucene內部會遍歷提取所有出現在文檔集合
的排序字段然后再次構建一個最終的排好序的文檔集合list,這個步驟的過程全部維持在內存中操作,而且如果排序數據量巨大的話,非常容易就造成solr內存溢出和性能緩慢。

基於這個原因,在lucene4.x之后出現了docvalue這個新特性,在構建索引時會對開啟docvalues的字段,額外構建一個已經排 好序的文檔到字段級別的一個列式存儲映射,它減輕了在排序和分組時,對內存的依賴,而且大大提升了這個過程的性能,當然它也會耗費的一定的磁盤空間。

(三)什么時候應該用DocValues?

通過上面的剖析,散仙相信大家已經對DocValues有一個初步的了解了,至於它的應用場景,那么也非常明顯了,總結起來主要以下幾個方面:

1,需要聚合的字段,包括sort,agg,group,facet等
2,需要提供函數查詢的字段
3,需要高亮的字段,這個確實能加速,但是散仙並不建議把高亮放在服務端程序做,建議放在前端實現,不容易出錯而且總體性能比服務端高
4,需要參與自定義評分的字段,這個稍復雜,大多數人的場景中,不一定能用到,后面會單獨寫一篇文章介紹。

對於不需要參與上面任何一項的字段,可以選擇關閉docvalues,這樣可以節省一定的磁盤空間.

(四)DocValues的種類

在lucene的枚舉類DocValuesType 中,我們可以看見它聲明了六個常量:
1,  NONE  不開啟docvalue時的狀態
2,  NUMERIC  單個數值類型的docvalue主要包括(int,long,float,double)
3,  BINARY    二進制類型值對應不同的codes最大值可能超過32766字節,
4,  SORTED  有序增量字節存儲,僅僅存儲不同部分的值和偏移量指針,值必須小於等於32766字節
5,  SORTED_NUMERIC   存儲數值類型的有序數組列表
6,  SORTED_SET     可以存儲多值域的docvalue值,但返回時,僅僅只能返回多值域的第一個docvalue

通常有四種docvalue存儲場景:

A: 字符串或UUID字段+單值 會選擇SORTED作為docvalue存儲
B: 字符串或UUID字段+多值 會選擇SORTED_SET作為docvalue存儲
C:數值或日期或枚舉字段+單值 會選擇NUMERIC 作為docvalue存儲
D:數值或日期或枚舉字段+多值 會選擇SORTED_SET作為docvalue存儲

注意,分詞字段存儲docvalue是沒有意義的

(五)如何在Lucene,Solr,ElasticSearch中使用DocValues?

說完了概念方面的東西,下面來點實例的例子,來看下如何給索引加上docsvalue,只要加上docvalues后,排序,分組,聚合的時候
會自動使用docvalue提速,所以我們關注的重點是如何激活docvalue。

1,在原生Lucene中使用DocValues,這個稍麻煩,需要自定義組裝,因為lucene是核心算法包,所以封裝程度並不是很高,正是
由於這樣,理解了lucene之后,再理解solr和elasticsearch是非常easy的。

下面是在lucene中存儲docvalue例子,一個是string類型,一個是數值類型,分詞類型在這里沒有意義,不再提及:

Java代碼   收藏代碼
  1. //數值存儲例子  
  2.   FieldType num=new FieldType();  
  3.   num.setStored(true);//設置存儲  
  4.   num.setIndexOptions(IndexOptions.DOCS);//設置索引類型  
  5.   num.setNumericType(NumericType.DOUBLE);//數值類型  
  6.   num.setDocValuesType(DocValuesType.NUMERIC);//DocValue類型  
  7.   
  8.   Document doc=new Document();  
  9.   //添加string字段  
  10.   doc.add(new SortedDocValuesField("id",new BytesRef("01011")));  
  11.   //添加數值類型的字段  Float,Doule需要額外轉成bit位才能存儲,Interger和Long則不需要  
  12.   doc.add(new DoubleField("price", Double.doubleToRawLongBits(25.258), num));  



如何讀取:

Java代碼   收藏代碼
  1. //讀取索引文件  
  2.  DirectoryReader reader=DirectoryReader.open(FSDirectory.open(Paths.get(indexDir)));  
  3.  //如果有多個段需要merge成一個,獲取第一個進行測試,本例中僅僅就有一個段  
  4.  SortedDocValues str = DocValues.getSorted(reader.leaves().get(0).reader(), "id");  
  5.  //數值類型  
  6.  NumericDocValues db = DocValues.getNumeric(reader.leaves().get(0).reader(), "price");  
  7.  //讀取字符串類型的ByteRef然后打印其內容  
  8.  System.out.println("id:"+str.get(0).utf8ToString());  
  9.  //注意此處,要與類型對應,如果是Float,則需要Float.intBitsToFloat((int)db.get(0))進行位數還原  
  10.  System.out.println("price: "+Double.longBitsToDouble(db.get(0)));  
  11.  reader.close();  




2,在Solr中docvalue默認是全部關閉,比較嚴謹,大家可酌情開啟

Java代碼   收藏代碼
  1. <fieldname="easy_money"type="double"indexed="true"stored="true"docValues="true"  />  



3,在ElasticSearch中,默認docvalue全部激活,比較簡單暴力,大家可酌情關閉一些不需要使用docvalue的字段,以節省磁盤空間

Java代碼   收藏代碼
  1. "session_id":{"type":"string","index":"not_analyzed","doc_values":false}  


摘自:http://qindongliang.iteye.com/blog/2297280


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM