Elasticsearch 中映射參數doc

一、doc_values

默認情況下,大部分字段是索引的,這樣讓這些字段可被搜索。倒排索引(inverted index)允許查詢請求在詞項列表中查找搜索項(search term),並立即獲得包含該詞項的文檔列表。

倒排索引(inverted index):

Elasticsearch 中映射參數doc_values 和 fielddata分析比較

如果我們想要獲得所有包含 brown 的文檔的詞的完整列表,我們會創建如下查詢:

GET /my_index/_search

{

"query" : {

"match" : {

"body" : "brown"

}

},

"aggs" : {

"popular_terms": {

"terms" : {

"field" : "body"

}

}

}

}

倒排索引是根據詞項來排序的,所以我們首先在詞項列表中找到 brown,然後掃描所有列,找到包含 brown 的文檔。我們可以快速看到 Doc_1 和 Doc_2 包含 brown 這個 token。

然後,對於聚合部分,我們需要找到 Doc_1 和 Doc_2 裡所有唯一的詞項。用倒排索引做這件事情代價很高: 我們會迭代索引裡的每個詞項並收集 Doc_1 和 Doc_2 列裡面 token。這很慢而且難以擴展:隨著詞項和文檔的數量增加,執行時間也會增加。

Doc values 通過轉置兩者間的關係來解決這個問題。倒排索引將詞項映射到包含它們的文檔,doc values 將文檔映射到它們包含的詞項:

Elasticsearch 中映射參數doc_values 和 fielddata分析比較

當數據被轉置之後,想要收集到 Doc_1 和 Doc_2 的唯一 token 會非常容易。獲得每個文檔行,獲取所有的詞項,然後求兩個集合的並集。

Doc values 可以使聚合更快、更高效並且內存友好。Doc values 的存在是因為倒排索引只對某些操作是高效的。

倒排索引的優勢:在於查找包含某個項的文檔,而對於從另外一個方向的相反操作並不高效,即:確定哪些項是否存在單個文檔裡,聚合需要這種訪問模式。


在 Elasticsearch 中,Doc Values 就是一種列式存儲結構,默認情況下每個字段的 Doc Values 都是激活的,Doc Values 是在索引時創建的。當字段索引時,Elasticsearch 為了能夠快速檢索,會把字段的值加入倒排索引中,同時它也會存儲該字段的 `Doc Values`。


Elasticsearch 中的 Doc Values 常被應用到以下場景:

  • 對一個字段進行排序
  • 對一個字段進行聚合
  • 某些過濾,比如地理位置過濾
  • 某些與字段相關的腳本計算


因為文檔值(doc values)被序列化到磁盤,我們可以依靠操作系統的幫助來快速訪問。當 working set 遠小於節點的可用內存,系統會自動將所有的文檔值保存在內存中,使得其讀寫十分高速;當其遠大於可用內存,操作系統會自動把 Doc Values 加載到系統的頁緩存中,從而避免了 jvm 堆內存溢出異常。


因此,搜索和聚合是相互緊密纏繞的。搜索使用倒排索引查找文檔,聚合操作收集和聚合 doc values 裡的數據。


doc values 支持大部分字段類型,但是text 字段類型不支持(因為analyzed)。

Elasticsearch 中映射參數doc_values 和 fielddata分析比較

(1) status_code 字段默認啟動 doc_values 屬性;

(2) session_id 顯式設置 doc_values = false,但是仍然可以被查詢;


如果確信某字段不需要排序或者聚合,或者從腳本中訪問字段值,那麼我們可以設置 doc_values = false,這樣可以節省磁盤空間。


二、fielddata

與 doc values 不同,fielddata 構建和管理 100% 在內存中,常駐於 JVM 內存堆。這意味著它本質上是不可擴展的。


fielddata可能會消耗大量的堆空間,尤其是在加載高基數(high cardinality)text字段時。一旦fielddata已加載到堆中,它將在該段的生命週期內保留。此外,加載fielddata是一個昂貴的過程,可能會導致用戶遇到延遲命中。這就是默認情況下禁用fielddata的原因。


如果需要對 text 類型字段進行排序、聚合、或者從腳本中訪問字段值,則會出現如下異常:

Fielddata is disabled on text fields by default. Set fielddata=true on [your_field_name] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory.

但是,在啟動fielddata 設置之前,需要考慮為什麼針對text 類型字段進行排序、聚合、或腳本呢?通常情況下,這是不太合理的。

text字段在索引時,例如New York,這樣的詞會被分詞,會被拆成new、york 2個詞項,這樣當搜索new 或 york時,可以被搜索到。在此字段上面來一個terms的聚合會返回一個new的bucket和一個york的bucket,但是你可能想要的是一個單一new york的bucket。


怎麼解決這一問題呢?

你可以使用 text 字段來實現全文本查詢,同時使用一個未分詞的 keyword 字段,且啟用doc_values,來處理聚合操作。

Elasticsearch 中映射參數doc_values 和 fielddata分析比較

(1) 使用my_field 字段用於查詢;

(2) 使用my_field.keyword 字段用於聚合、排序、或腳本;


可以使用 PUT mapping API 在現有text 字段上啟用 fielddata,如下所示:

Elasticsearch 中映射參數doc_values 和 fielddata分析比較


分享到:


相關文章: