浅谈Lucene中的DocValues
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
前言: 在Lucene4.x之后,出現(xiàn)一個(gè)重大的特性,就是索引支持DocValues,這對(duì)于廣大的solr和elasticsearch用戶,無(wú)疑來(lái)說(shuō)是一個(gè)福音,這玩意的出現(xiàn)通過(guò)犧牲一定的磁盤(pán)空間帶來(lái)的好處主要有兩個(gè): (1)節(jié)省內(nèi)存 (2)對(duì)排序,分組和一些聚合操作時(shí)能夠大大提升性能
下面來(lái)詳細(xì)介紹下DocValue的原理和使用場(chǎng)景
(一)什么是DocValues?
DocValues其實(shí)是Lucene在構(gòu)建索引時(shí),會(huì)額外建立一個(gè)有序的基于document => field value的映射列表;
(二)為什么要用DocValues ?
基于lucene的solr和es都是使用經(jīng)典的倒排索引模式來(lái)達(dá)到快速檢索的目的,簡(jiǎn)單的說(shuō)就是建立 搜索詞=》 文檔id列表 這樣的關(guān)系映射, 然后在搜索時(shí),通過(guò)類似hash算法,來(lái)快速定位到一個(gè)搜索關(guān)鍵詞,然后讀取其的文檔id集合,這就是倒排索引的核心思想,這樣搜索數(shù)據(jù) 是非常高效快速的,當(dāng)然它也是有缺陷的,假如我們需要對(duì)數(shù)據(jù)做一些聚合操作,比如排序,分組時(shí),lucene內(nèi)部會(huì)遍歷提取所有出現(xiàn)在文檔集合 的排序字段然后再次構(gòu)建一個(gè)最終的排好序的文檔集合list,這個(gè)步驟的過(guò)程全部維持在內(nèi)存中操作,而且如果排序數(shù)據(jù)量巨大的話,非常容易就造成solr內(nèi)存溢出和性能緩慢。
基于這個(gè)原因,在lucene4.x之后出現(xiàn)了docvalue這個(gè)新特性,在構(gòu)建索引時(shí)會(huì)對(duì)開(kāi)啟docvalues的字段,額外構(gòu)建一個(gè)已經(jīng)排好序的文檔到字段級(jí)別的一個(gè)列式存儲(chǔ)映射,它減輕了在排序和分組時(shí),對(duì)內(nèi)存的依賴,而且大大提升了這個(gè)過(guò)程的性能,當(dāng)然它也會(huì)耗費(fèi)的一定的磁盤(pán)空間。
(三)什么時(shí)候應(yīng)該用DocValues?
通過(guò)上面的剖析,散仙相信大家已經(jīng)對(duì)DocValues有一個(gè)初步的了解了,至于它的應(yīng)用場(chǎng)景,那么也非常明顯了,總結(jié)起來(lái)主要以下幾個(gè)方面:
1,需要聚合的字段,包括sort,agg,group,facet等 2,需要提供函數(shù)查詢的字段 3,需要高亮的字段,這個(gè)確實(shí)能加速,但是散仙并不建議把高亮放在服務(wù)端程序做,建議放在前端實(shí)現(xiàn),不容易出錯(cuò)而且總體性能比服務(wù)端高 4,需要參與自定義評(píng)分的字段,這個(gè)稍復(fù)雜,大多數(shù)人的場(chǎng)景中,不一定能用到,后面會(huì)單獨(dú)寫(xiě)一篇文章介紹。
對(duì)于不需要參與上面任何一項(xiàng)的字段,可以選擇關(guān)閉docvalues,這樣可以節(jié)省一定的磁盤(pán)空間.
(四)DocValues的種類
在lucene的枚舉類DocValuesType 中,我們可以看見(jiàn)它聲明了六個(gè)常量: 1, NONE 不開(kāi)啟docvalue時(shí)的狀態(tài) 2, NUMERIC 單個(gè)數(shù)值類型的docvalue主要包括(int,long,float,double) 3, BINARY 二進(jìn)制類型值對(duì)應(yīng)不同的codes最大值可能超過(guò)32766字節(jié), 4, SORTED 有序增量字節(jié)存儲(chǔ),僅僅存儲(chǔ)不同部分的值和偏移量指針,值必須小于等于32766字節(jié) 5, SORTED_NUMERIC 存儲(chǔ)數(shù)值類型的有序數(shù)組列表 6, SORTED_SET 可以存儲(chǔ)多值域的docvalue值,但返回時(shí),僅僅只能返回多值域的第一個(gè)docvalue
通常有四種docvalue存儲(chǔ)場(chǎng)景:
A: 字符串或UUID字段+單值 會(huì)選擇SORTED作為docvalue存儲(chǔ) B: 字符串或UUID字段+多值 會(huì)選擇SORTED_SET作為docvalue存儲(chǔ) C:數(shù)值或日期或枚舉字段+單值 會(huì)選擇NUMERIC 作為docvalue存儲(chǔ) D:數(shù)值或日期或枚舉字段+多值 會(huì)選擇SORTED_SET作為docvalue存儲(chǔ)
注意,分詞字段存儲(chǔ)docvalue是沒(méi)有意義的
(五)如何在Lucene,Solr,ElasticSearch中使用DocValues?
說(shuō)完了概念方面的東西,下面來(lái)點(diǎn)實(shí)例的例子,來(lái)看下如何給索引加上docsvalue,只要加上docvalues后,排序,分組,聚合的時(shí)候 會(huì)自動(dòng)使用docvalue提速,所以我們關(guān)注的重點(diǎn)是如何激活docvalue。
1,在原生Lucene中使用DocValues,這個(gè)稍麻煩,需要自定義組裝,因?yàn)閘ucene是核心算法包,所以封裝程度并不是很高,正是 由于這樣,理解了lucene之后,再理解solr和elasticsearch是非常easy的。
下面是在lucene中存儲(chǔ)docvalue例子,一個(gè)是string類型,一個(gè)是數(shù)值類型,分詞類型在這里沒(méi)有意義,不再提及:
Java代碼 收藏代碼 //數(shù)值存儲(chǔ)例子
FieldType num=new FieldType();
num.setStored(true);//設(shè)置存儲(chǔ)
num.setIndexOptions(IndexOptions.DOCS);//設(shè)置索引類型
num.setNumericType(NumericType.DOUBLE);//數(shù)值類型
num.setDocValuesType(DocValuesType.NUMERIC);//DocValue類型
Document doc=new Document();
//添加string字段
doc.add(new SortedDocValuesField("id",new BytesRef("01011")));
//添加數(shù)值類型的字段 Float,Doule需要額外轉(zhuǎn)成bit位才能存儲(chǔ),Interger和Long則不需要
doc.add(new DoubleField("price", Double.doubleToRawLongBits(25.258), num));
如何讀取:
Java代碼 收藏代碼 //讀取索引文件
DirectoryReader reader=DirectoryReader.open(FSDirectory.open(Paths.get(indexDir)));
//如果有多個(gè)段需要merge成一個(gè),獲取第一個(gè)進(jìn)行測(cè)試,本例中僅僅就有一個(gè)段
SortedDocValues str = DocValues.getSorted(reader.leaves().get(0).reader(), "id");
//數(shù)值類型
NumericDocValues db = DocValues.getNumeric(reader.leaves().get(0).reader(), "price");
//讀取字符串類型的ByteRef然后打印其內(nèi)容
System.out.println("id:"+str.get(0).utf8ToString());
//注意此處,要與類型對(duì)應(yīng),如果是Float,則需要Float.intBitsToFloat((int)db.get(0))進(jìn)行位數(shù)還原
System.out.println("price: "+Double.longBitsToDouble(db.get(0)));
reader.close();
2,在Solr中docvalue默認(rèn)是全部關(guān)閉,比較嚴(yán)謹(jǐn),大家可酌情開(kāi)啟
Java代碼 收藏代碼 <fieldname="easy_money"type="double"indexed="true"stored="true"docValues="true" />
3,在ElasticSearch中,默認(rèn)docvalue全部激活,比較簡(jiǎn)單暴力,大家可酌情關(guān)閉一些不需要使用docvalue的字段,以節(jié)省磁盤(pán)空間
Java代碼 收藏代碼 "session_id":{"type":"string","index":"not_analyzed","doc_values":false}
最后再提一點(diǎn),在和solr和es中,如果想要在自己寫(xiě)的插件中讀取docvalue的值,讀取方法和lucene的差不多,需要注意doule和float的的值轉(zhuǎn)換。
有什么問(wèn)題可以掃碼關(guān)注微信公眾號(hào):我是攻城師(woshigcs),在后臺(tái)留言咨詢。 技術(shù)債不能欠,健康債更不能欠, 求道之路,我們同行。
轉(zhuǎn)載于:https://my.oschina.net/u/1027043/blog/673016
總結(jié)
以上是生活随笔為你收集整理的浅谈Lucene中的DocValues的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: javascript 常用校验代码 2
- 下一篇: Android Studio 提示与技巧