ElasticSearch Doc Values和Fielddata詳解

一、Doc Values介紹

倒排索引在搜索包含指定 term 的文檔時效率極高,但在執行相反操作,比如查詢一個文檔中包含哪些 term,以及進行排序、聚合等與指定字段相關的操作時,表現就很差了,這時候就需要用到 Doc Values。

倒排索引是將 term 映射到包含它們的文檔,而 Doc Values 則是將文檔映射到它們包含的所有詞項,以下是一個示例:

Doc

Terms

Doc_1

brown, dog, fox, jumped, lazy, over, quick, the

Doc_2

brown, dogs, foxes, in, lazy, leap, over, quick, summer

Doc_3

dog, dogs, fox, jumped, over, quick, the

當數據被這樣倒置之后,想要收集到 Doc_1 和 Doc_2 的唯一 token 就變得非常容易。只需要獲取每個文檔行,提取所有的詞項,然后求兩個集合的并集即可。

其實,Doc Values 本質上是一個序列化了的列式存儲結構,這種結構非常適合排序、聚合以及字段相關的腳本操作。而且這種存儲方式便于壓縮,尤其是對于數字類型,壓縮后能夠大大減少磁盤空間占用,同時提升訪問速度。下面是一個數字類型的 Doc Values 示例:

Doc

Terms

Doc_1

100

Doc_2

1000

Doc_3

1500

Doc_4

1200

Doc_5

300

Doc_6

1900

Doc_7

4200

列式存儲意味著這些數據會形成一個連續的數據塊:[100, 1000, 1500, 1200, 300, 1900, 4200]。因為我們知道它們都是數字(不像文檔或行中看到的異構集合),所以可以使用統一的偏移量來將它們緊密排列。

而且,針對這樣的數字有很多種壓縮技巧。你會注意到這里每個數字都是 100 的倍數,Doc Values 會檢測一個段里面的所有數值,并使用一個最大公約數來進行進一步的數據壓縮。比如在這個例子中,可以用 100 作為公約數,那么以上數字就變為 [1, 10, 15, 12, 3, 19, 42],這樣可用很少的 bit 就能存儲,節約了磁盤空間。一般來說,Doc Values 按順序檢測以下壓縮方案:

  • 如果所有值都相同(或缺失),就設置一個標志并記錄該值

  • 如果少于 256 個值,就會使用一個簡易碼表

  • 如果值個數大于 256,就檢查是否存在最大公約數

  • 如果沒有最大公約數,就以偏移量的方式從最小值開始對所有值編碼

String 類型使用順序表,按和數字類型類似的方式編碼。String 類型去重后排序,然后寫入一個表中,并分配一個 ID 號,這些 ID 號就被當做數字類型的 Doc Values。這意味著字符串享有許多與數字相同的壓縮特點。

Doc Values 是在字段索引時與倒排索引同時生成,并且生成以后是不可變的。

Doc Value 默認對除了 analyzed String 外的所有字段啟用(因為分詞后會生成很多 token 使得 Doc Values 效率降低)。但是當你知道某些字段永遠不會進行排序、聚合以及腳本操作的時候,可以禁用 Doc Values 以節約磁盤空間、提升索引速度,示例如下:

PUT my_index{"mappings": {"my_type": {"properties": {"session_id": {"type": "string","index": "not_analyzed","doc_values": false}}}}}

以上配置后,session_id 字段就只能被搜索,不能被用于排序、聚合以及腳本操作了。

還可以通過設定 doc_values 為 true,index 為 no 來讓字段不能被搜索但可以用于排序、聚合以及腳本操作:

PUT my_index{"mappings": {"my_type": {"properties": {"customer_token": {"type": "string","index": "not_analyzed","doc_values": true,"index": "no"}}}}}

Doc Value 的特點是快速、高效、內存友好,使用由 linux kernel 管理的文件系統緩存彈性存儲。doc values 在排序、聚合或與字段相關的腳本計算中得到了高效運用,任何需要查找某個文檔包含的值的操作都必須使用它。如果你確定某個 field 不會做字段相關操作,可以直接關掉 doc_values,節約內存,加快訪問速度。

二、Fielddata介紹

上文說過,在排序、聚合以及在腳本中訪問 field 值時,需要一種與倒排索引截然不同的數據訪問模式:不同于倒排索引中的查找 term-> 找到對應 docs 的過程,我們需要直接查找 doc 然后找到指定某個 field 中包含的 terms。

大多數 field 使用索引時、磁盤上的 doc_values 來支持這種訪問模式,但是分詞了的 String field 不支持 Doc Values,而是使用一種叫 FieldData 的數據結構。

FieldData 主要是針對 analyzed String,它是一種查詢時(query-time)的數據結構。

FieldData 緩存主要應用場景是在對某一個 field 排序或者計算類的聚合運算時。它會把這個 field 列的所有值加載到內存,目的是提供對這些值的快速文檔訪問。為 field 構建 FieldData 緩存可能會很昂貴,因此建議有足夠的內存來分配它,并保持其處于已加載狀態。

FieldData 是在第一次將該 field 用于聚合、排序或在腳本中訪問時按需構建。FieldData 是通過從磁盤讀取每個段來讀取整個反向索引,然后逆置 term->doc 的關系,并將結果存儲在 JVM 堆中構建的。所以,加載 FieldData 是開銷很大的操作,一旦它被加載后,就會在整個段的生命周期中保留在內存中。

這里可以注意下 FieldData 和 Doc Values 的區別。較早的版本中,其他數據類型也是用的 FieldData,但是目前已經用隨文檔索引時創建的 Doc Values 所替代。

JVM 堆內存資源非常寶貴,能用好它對系統的高效穩定運行至關重要。FieldData 直接放在堆內,所以必須合理設定用于存放它的堆內存資源數。ES 中控制 FieldData 內存使用的參數是 indices.fielddata.cache.size,可以用 x% 表示占該節點堆內存百分比,也可以用如 12GB 這樣的數值。默認狀況下,這個設置是無限制的,ES 不會從 FieldData 中驅逐數據。如果生成的 fielddata 大小超過指定的 size,則將驅逐其他值以騰出空間。使用時一定要注意,這個設置只是一個安全策略而并非內存不足的解決方案。因為通過此配置觸發數據驅逐,ES 會立刻開始從磁盤加載數據,并把其他數據驅逐以保證有足夠空間,導致很高的 IO 以及大量的需要被垃圾回收的內存垃圾。

舉個例子:每天為日志文件建一個新的索引。一般來說我們只對最近幾天數據感興趣,很少查詢老數據。但是,按默認設置 FieldData 中的老索引數據是不會被驅逐的。這樣的話,FieldData 就會一直持續增長直到觸發熔斷機制,這個機制會讓你再也不能加載更多的 FieldData 到內存。在這種場景下,你只能對老的索引訪問 FieldData,但不能加載更多新數據。所以,這個時候就可以通過以上配置來把最近最少使用的 FieldData 驅逐,為新進來的數據騰空間。

FieldData 是在數據被加載后再檢查的,那么如果一個查詢導致嘗試加載超過可用內存的數據,就會導致 OOM 異常。ES 中使用了 FieldData Circuit Breaker 來處理上述問題,它可以通過分析一個查詢涉及到的字段的類型、基數、大小等來評估所需內存。如果估計的查詢大小大于配置的堆內存使用百分比限制,則斷路器會跳閘,查詢將被中止并返回異常。

斷路器是在數據加載前工作的,所以你不用擔心遇到 FieldData 導致的 OOM 異常。ES 擁有多種類型的斷路器:

  • indices.breaker.fielddata.limit

  • indices.breaker.request.limit

  • indices.breaker.total.limit

可以根據實際需要進行配置。

FieldData 是為分詞 String 而生,它會消耗大量的 java 堆空間,特別是加載基數(cardinality)很大的分詞 String field 時。但是往往對這種類型的分詞 Field 做聚合是沒有意義的。

值得注意的是,FieldData 和 Doc Values 的加載時機不同,前者是首次查詢時,后者是 doc 索引時。還有一點,FieldData 是按每個段來緩存的。

三、Doc values 與 Fielddata 對比

doc_values 與 fielddata 一個很顯著的區別是,前者的工作地盤主要在磁盤,而后者的工作地盤在內存。

維度

doc_values

fielddata

創建時間

index 時創建

使用時動態創建

創建位置

磁盤

內存 (jvm heap)

優點

不占用內存空間

不占用磁盤空間

缺點

索引速度稍低

文檔很多時,動態創建開銷大,且占內存

索引速度稍低是相對于 fielddata 方案而言的,其實仔細想想也可以理解。拿排序舉例,一個在磁盤排序,一個在內存排序,誰的速度快不言而喻。

而且,隨著 ES 版本的升級,對于 doc_values 的優化越來越好,索引的速度已經很接近 fielddata 了,而且我們知道硬盤的訪問速度也是越來越快(比如 SSD)。所以 doc_values 現在可以滿足大部分場景,也是 ES 官方重點維護的對象。

所以,doc values 相比 field data 還是有很多優勢的。因此 ES2.x 之后,支持聚合的字段屬性默認都使用 doc_values,而不是 fielddata。

Global Ordinals 全局序號

Global Ordinals 是一個在 Doc Values 和 FieldData 之上的數據結構,它為每個唯一的 term 按字典序維護了一個自增的數字序列。每個 term 都有自己的一個唯一數字,而且字母 A 的全局序號小于字母 B。特別注意,全局序號只支持 String 類型的 field。

需要注意的是,Doc Values 和 FieldData 也有自己的 ordinals 序號,這個序號是特定 segment 和 field 中的唯一編號。通過提供 Segment Ordinals 和 Global Ordinals 間的映射關系,全局序號在此基礎上創建,后者(即全局序號)在整個 shard 分片中是唯一的。

一個特定字段的 Global Ordinals 跟一個分片中的所有段相關,而 Doc Values 和 FieldData 的 ordinals 只跟單個段相關。因此,只要是一個新段要變得可見,就必須完全重建全局序號。

也就是說,跟 FieldData 一樣,在默認情況下全局序號也是懶加載的,會在第一個請求 FieldData 命中一個索引時來構建全局序號。實際上,在為每個段加載 FieldData 后,ES 就會創建一個稱為 Global Ordinals(全局序號)的數據結構,來構建一個由分片內的所有段中的唯一 term 組成的列表。

全局序號的內存開銷小,原因是它由非常高效的壓縮機制。提前加載的全局序號可以將加載時間從第一次搜索時轉到全局序號刷新時。

全局序號的加載時間依賴于一個字段中的 term 數量,但是總的來說耗時較低,因為來源的字段數據都已經加載到內存了。

全局序號在用到段序號的時候很有用,比如排序或者 terms aggregation,可以提升執行效率。

我們舉個簡單的例子。比如有十億級別的 doc,每個 doc 都有一個 status 字段,但只有 pending、published、deleted 三個狀態數據。如果直接存整個 String 數據到內存,那么就算每個 doc 有 15 字節,一共就是差不多 14GB 的數據。怎么減少占用空間呢?首先想到的就是用數字來進行編碼,碼表如下:

Ordinal

Term

0

status_deleted

1

status_pending

2

status_published

這樣的話,初始的那三個 String 就只在碼表內被存了一次。FieldData 中的 doc 就可以直接用編碼來指向實際值:

Doc

Ordinal

0

1 # pending

1

1 # pending

2

2 # published

3

0 # deleted

這樣編碼以后,直接把數據量壓縮了十倍左右。但有個問題是 FieldData 是按每個段來分別加載、緩存的。那么就會出現一種情況,如果一個段內的 doc 只有 deleted 和 published 兩個狀態,那么就會導致該 FieldData 算出來的碼表只有 0 和 1,這就和擁有 3 個狀態的段算出的 FieldData 碼表不同。這樣的話,聚合的時候就必須一個段一個段的計算,最后再聚合,十分緩慢,開銷巨大。

ES 的做法是用 Global Ordinals 這種構建在 FieldData 之上的小巧數據結構,編碼會結合所有段來計算唯一值然后存放為一個序號碼表。這樣一來,term aggregation 可以只在全局序號上進行聚合,而且只會在聚合的最終階段來計算從序號到真實的 String 值一次。這個機制可以提升聚合的性能 3-4 倍。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/92238.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/92238.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/92238.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【C語言】解決VScode中文亂碼問題

文章目錄【C語言】解決VScode中文亂碼問題彈出無法寫入用戶設置的處理方法彈出無法在只讀編輯器編輯的問題處理方法【C語言】解決VScode中文亂碼問題 💬歡迎交流:在學習過程中如果你有任何疑問或想法,歡迎在評論區留言,我們可以共…

MySQL筆記4

一、范式1.概念與意義范式(Normal Form)是數據庫設計需遵循的規范,解決“設計隨意導致后期重構困難”問題。主流有 三大范式(1NF、2NF、3NF),還有進階的 BCNF、4NF、5NF 等,范式間是遞進依賴&am…

切比雪夫不等式的理解以及推導【超詳細筆記】

文章目錄參考教程一、意義1. 正態分布的 3σ 法則2. 不等式的含義3. 不等式的意義二、不等式的證明1. 馬爾科夫不等式馬爾可夫不等式證明(YYY 為非負隨機變量 )2. 切比雪夫不等式推導參考教程 一個視頻,徹底理解切比雪夫不等式 一、意義 1. 正態分布的…

Spring Boot Jackson 序列化常用配置詳解

一、引言在當今的 Web 開發領域,JSON(JavaScript Object Notation)已然成為數據交換的中流砥柱。無論是前后端分離架構下前后端之間的數據交互,還是微服務架構里各個微服務之間的通信,JSON 都承擔著至關重要的角色 。它…

Jetpack ViewModel LiveData:現代Android架構組件的核心力量

引言在Android應用開發中,數據管理和界面更新一直是開發者面臨的重大挑戰。傳統的開發方式常常導致Activity和Fragment變得臃腫,難以維護,且無法優雅地處理配置變更(如屏幕旋轉)。Jetpack中的ViewModel和LiveData組件正…

Python數據分析案例79——基于征信數據開發信貸風控模型

背景 雖然模型基本都是表格數據那一套了,算法都沒什么新鮮點,但是本次數據還是很值得寫個案例的,有征信數據,各種,個人,機構,逾期匯總..... 這么多特征來做機器學習模型應該還不錯。本次帶來&…

板凳-------Mysql cookbook學習 (十二--------3_2)

3.3鏈接表 結構 P79頁 用一個類圖來表示EmployeeNode類的結構,展示其屬性和關系: plaintext ----------------------------------------- | EmployeeNode | ----------------------------------------- | - emp_no: int …

深度學習圖像預處理:統一輸入圖像尺寸方案

在實際訓練中,最常見也最簡單的做法,就是在送入網絡前把所有圖片「變形」到同一個分辨率(比如 256256 或 224224),或者先裁剪/填充成同樣大小。具體而言,可以分成以下幾類方案:一、圖…

pytest-log

問題1:我們在運行測試用例的時候如何記錄測試的log,如何使用?問題2:我寫的函數,為了方便log記錄,但是在pytest運行時,會兼容pytest且不會重復記錄,怎么解決?1、pytest有內…

在安卓源碼中添加自定義jar包給源碼中某些模塊使用

一、具體步驟 1. 準備目錄與 Jar 包 在vendor下 創建新的模塊目錄,放入demo.jar 包: demojar/ # 模塊目錄 ├── Android.bp # 編譯配置文件 └── demo.jar 2. 編寫 Android.bp 配置 Android.bp 示例配置: java_import {…

buntu 22.04 上離線安裝Docker 25.0.5(二)

以下有免費的4090云主機提供ubuntu22.04系統的其他入門實踐操作 地址:星宇科技 | GPU服務器 高性能云主機 云服務器-登錄 相關兌換碼星宇社區---4090算力卡免費體驗、共享開發社區-CSDN博客 兌換碼要是過期了,可以私信我獲取最新兌換碼!&a…

初探 Web 環境下的 LLM 安全:攻擊原理與風險邊界

文章目錄前言1 什么是大型語言模型(LLM)?1.1 LLM的核心特征1.2 LLM在Web場景中的典型應用2 LLM攻擊的核心手段:提示注入與權限濫用3 LLM與API集成的安全隱患:工作流中的漏洞節點3.1 LLM-API集成的典型工作流3.2 工作流…

【新手向】PyTorch常用Tensor shape變換方法

【新手向】PyTorch常用Tensor shape變換方法 前言 B站UP主科研水神大隊長的視頻中介紹了“縫合模塊”大法,其中專門強調了“深度學習 玩的就是shape”。受此啟發,專門整理能夠調整tensor形狀的幾個內置函數,方便以后更好地調整PyTorch代碼中的…

React 18 vs Vue3:狀態管理方案深度對比

?? 背景: React有Redux、Zustand、Jotai等方案 Vue有Pinia、Vuex 4.x 如何選擇適合項目的方案? ?? 核心對比: 維度 React (Redux Toolkit) Vue3 (Pinia) 類型安全 ? 需手動配置TS ? 自動類型推導 代碼量 較多(需寫action) 較少(類似Vuex 5) 響應式原理 不可變數據…

UE5網絡聯機函數

Find Sessions Create Session Join Session Destroy Session Steam是p2p直接聯機 一、steam提供的測試用AppId AppId是steam為每一款游戲所設定的獨有標識,每一款要上架steam的游戲都會擁有獨一無二的AppId。不過為了方便開發者測試,steam提供了游…

Spring Boot 監控:AOP vs Filter vs Java Agent

01前言 在 高并發 微服務 中, 傳統 手動埋點(System.currentTimeMillis())就像用體溫計量火箭速度——代碼侵入、重復勞動、維護爆炸。 下文是無侵入、高精度、全鏈路 監控 API 耗時,全程不碰業務代碼的方案! 02實戰&…

基于Android的電子記賬本系統

博主介紹:java高級開發,從事互聯網行業多年,熟悉各種主流語言,精通java、python、php、爬蟲、web開發,已經做了多年的畢業設計程序開發,開發過上千套畢業設計程序,沒有什么華麗的語言&#xff0…

7月17日日記

結束了數學建模之后的這兩天一直在緊張的復習,但是說實話效率有點低,因為可能覺得自己找到了兩個小時速成課,覺得無所謂了,所以有點放松了。在宿舍杰哥和林雨城卻一直在復習,感覺他們的微積分和線性代數復習的都比我好…

Linux下SPI設備驅動開發

一.SPI協議介紹1.硬件連接介紹引腳含義:DO(MOSI):Master Output, Slave Input,SPI主控用來發出數據,SPI從設備用來接收數據。DI(MISO):Master Input, Slave Output,SPI主控用來發出數據,SPI從設…

用Dify構建氣象智能體:從0到1搭建AI工作流實戰指南

作為一名Agent產品經理,我最近在負責氣象智能體的建設項目。傳統氣象服務面臨三大痛點:數據孤島嚴重(氣象局API、衛星云圖、地面觀測站等多源數據格式不一)、響應鏈路長(從數據采集到預警發布需人工介入多個環節)、交互體驗單一(用戶只能被動接收標準化預警,無法個性化…