淺談Lucene中的DocValues

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

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

(一)什麼是DocValues?數組

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

(二)爲何要用DocValues ?session

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

基於這個緣由,在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代碼 收藏代碼 //數值存儲例子
FieldType num=new FieldType();
num.setStored(true);//設置存儲
num.setIndexOptions(IndexOptions.DOCS);//設置索引類型
num.setNumericType(NumericType.DOUBLE);//數值類型
num.setDocValuesType(DocValuesType.NUMERIC);//DocValue類型

Document doc=new Document();
//添加string字段
doc.add(new SortedDocValuesField("id",new BytesRef("01011")));
//添加數值類型的字段 Float,Doule須要額外轉成bit位才能存儲,Interger和Long則不須要
doc.add(new DoubleField("price", Double.doubleToRawLongBits(25.258), num));

如何讀取:

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

2,在Solr中docvalue默認是所有關閉,比較嚴謹,你們可酌情開啓

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

3,在ElasticSearch中,默認docvalue所有激活,比較簡單暴力,你們可酌情關閉一些不須要使用docvalue的字段,以節省磁盤空間

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

最後再提一點,在和solr和es中,若是想要在本身寫的插件中讀取docvalue的值,讀取方法和lucene的差很少,須要注意doule和float的的值轉換。

有什麼問題能夠掃碼關注微信公衆號:我是攻城師(woshigcs),在後臺留言諮詢。 技術債不能欠,健康債更不能欠, 求道之路,咱們同行。

相關文章
相關標籤/搜索