ES doc_values介紹2——本質是field value的列存儲,做聚合分析用,ES默認開啟,會占用存儲空間


一、doc_values介紹

doc values是一個我們再三重復的重要話題了,你是否意識到一些東西呢?

  • 搜索時,我們需要一個“詞”到“文檔”列表的映射
  • 排序時,我們需要一個“文檔”到“詞“列表的映射,換句話說,我們需要一個在倒排索引的基礎上建立的“正排索引”

這里的“正排索引”結構通常在其他系統中(如關系型數據庫)被稱為“列式存儲”。本質上,它是在數據字段的一列上存儲所有value,這種結構在某些操作上會表現得很高效,比如排序。

在ES里這種“列式存儲”就是我們熟悉的“doc values”,默認情況下它是被啟用的,doc values在index-time(索引期)被創建:當一個字段被索引時,ES會把“詞”加入到倒排索引中,同時把這些詞也加入到面向“列式存儲”的doc values中(存儲在硬盤上)。

doc values通常被應用在以下幾個方面:

  • 基於一個字段排序
  • 基於一個字段聚合
  • 執行某些filter上(如:geolocation filter)
  • 在script(腳本)中引用了一個或多個字段

由於doc values在索引期被序列化到硬盤上,我們可以利用操作系統去快速的訪問它們,關於doc values在磁盤上是如何被管理的,后面會講到。

大多數的字段默認情況下都會被索引,這使得他們可以被搜索到,倒排索引允許一個查詢基於一個詞表排序,也可以快速訪問包含某個詞的文檔列表。

排序、聚合,和在腳本中訪問一些字段值時都需要另一種不同的訪問方式,因為倒排索引不支持這種訪問,所以我們需要一種結構能查詢到文檔到詞的映射。

doc values是在索引期創建基於磁盤的數據結構,這種結構使得上述訪問成為可能。doc values支持絕大部分字段類型,除了“analyzed”類型的string字段。(因為對於string的列存儲會先轉換為數字id再存)

所有的字段都默認支持doc values,如果你確定你不需要在某個字段上排序或者聚合或者在腳本中訪問,你可以disable掉:

  1. status_code字段默認開啟doc_values
  2. session_id字段禁用了doc_values,雖然被禁用但是還是可以被查詢

TIP:doc_values可以在同一個索引的同名字段上設置不同值,它也可以基於一個已存在的字段使用put mapping api來禁用它。

看如下的倒排索引結構:


如果我們想為每一個包含“brown”的文檔編輯一份完整的詞列表,我們可能會用如下查詢:

看上面的查詢部分。倒排索引通過詞條排好了序,所以我們首先找到包含“brown”的詞條列表,然后跨列掃描所有包含“brown”的文檔,這里我們很幸運的找到了“Doc_1”和“Doc_2”。

然后在聚合部分,我們需要找Doc_1和Doc_2中找到所有的詞,在倒排索引的去做這個操作很非常昂貴的:意味着我們不得不迭代索引中的每一個詞,看它們是否包含在doc_1和doc_2中,這個過程是非常緩慢的,而且也是非常傻逼的:因為隨着文檔詞量的增加,我們聚合的執行時間也會增加。

讓我們看看下面的結構:

有了這個結構我們就會很容易得到doc_1和doc_2所包含的詞條,我們只需要通過上面的結構把兩個集合合並起來就行了。
因此,查詢和聚合是非常復雜的,查詢文檔使用的是倒排索引,聚合文檔使用的是正排索引(doc_values)

note:doc values不僅僅是用在聚合中,還被用在排序、腳本、子父文檔關系(這里暫不做介紹)。

二、深入Doc Values

前面講到的doc values給我們幾個印象:快速訪問、高效、基於硬盤。現在我們來看看doc values到底是如何工作的?

doc values是在“索引期“隨着倒排索引一起生成的,也就是說doc values是基於每個索引段生成且是不可改變的(immutable),和倒排索引一樣,doc values也會被序列化到磁盤上,這使得它具有了高效性和可擴展性。

通過序列化一個數據結構到磁盤上,我們可以依賴操作系統的file system cache 替代JVM的堆內存,當我們的“工作集”小於OS可用內存時,操作系統會自然的加載這些doc values到內存。這時doc values的性能和在JVM堆內存中表現是一樣的。

但是當工作集大於操作系統可用內存時,操作系統將會按需加載doc values,這種情況下的訪問速度會明顯的慢於全量加載doc values的時候。但這種操作使得我們的服務器內存利用率遠超過服務器最大內存限制。試想一下,如果全量加載到doc values到內存中勢必會造成ES OutOfMemery。

NOTE:由於doc values不受JVM堆內存管理,所以我們可以把ES對內存設置得小一點,把更多的內存留給操作系統來換出(doc values),同時這也可以使JVM的GC工作在更小的堆內存上,更快更高效的執行GC。
通常,我們配置JVM的堆內存基本和操作系統內存各占一半(50%),由於引進了doc values所以我們可以考慮把JVM的堆內存設置得小一些,比如我們可以在一個64G的服務器上設置JVM堆內存為4 – 16GB比設置堆內存為32G更加高效。

三、Column-store compression(列式存儲壓縮)

本質上doc values是一個被序列化的面向“列式儲存”的結構,我們前面討論過列式存儲在某些查詢操作上是有優勢的,不僅如此它們也更擅長數據壓縮,特別是數字,這對磁盤存儲和快速訪問來說是及其重要的。

為了了解它是如何壓縮數據的,我們看下面簡單的doc values結構

像上面這種每行一條數據的形式,我們可以得到連續的數字塊,如:[100,1000,1500,1200,300,1900,4200]。因為我們知道它們都是數字值可以被排列在一起通過一個一致的偏移量。

跟深層次的,這里有幾種壓縮方法可以運用在這些數字上。你可能知道上面的數字都是100的倍數,如果索引段上所有的的數字都共享一個“最大公約數”,那么就可以用這個最大公約數去壓縮數據。如上面的數字我們可以除以100,得到的數據是[1,10,15,12,3,19,42]。這樣這些數字會變得小一些,存儲時占用的比特數也會小一些。

doc values使用幾種手段來壓縮數字。

  1. 如果所有的數字值都相等(或者缺失),會設置一個標記來表示該值
  2. 如果所有數字值的個數小於256個,將會使用一個簡單的編碼表來壓縮
  3. 如果大於了256個,看看是否存在最大公約數,存在則使用最小公倍數壓縮
  4. 如果不存在最大公約數,則存儲偏移量來壓縮數字。

如你看到的,你可能會想“這樣做對數值型字段做壓縮確實很好,那么對字符串類型呢?”,其實字符串壓縮也是和數字壓縮一樣采用同樣的方法通過一個序數表來壓縮,字符串被去重、排序后被賦予了一個ID,這些ID就是數字,這樣就可以采用上面的方案進行壓縮了。對於序數表本身也會采用壓縮存儲。

轉自:http://h2ex.com/1631


免責聲明!

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



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