Elasticsearch系列---聚合查詢原理
概要
本篇主要介紹聚合查詢的內部原理,正排索引是如何建立的和優化的,fielddata的使用,最後簡單介紹了聚合分析時如何選用深度優先和廣度優先。
正排索引
聚合查詢的內部原理是什麼,Elastichsearch是用什麼樣的資料結構去執行聚合的?用倒排索引嗎?
工作原理
我們瞭解到倒排索引對搜尋是非常高效的,但是在排序或聚合操作方面,倒排索引就顯得力不從心,例如我們舉個實際案例,假設我們有兩個文件:
- I have a friend who loves smile
- love me, I love you
為了建立倒排索引,我們先按最簡單的用空格把每個單詞分開,可以得到如下結果:
*表示該列文件中有這個詞條,為空表示沒有該詞條
Term | doc1 | doc2 |
---|---|---|
I | * | * |
have | * | |
a | * | |
friend | * | |
who | * | |
loves | * | |
smile | * | |
love | * | |
me | * | |
you | * |
如果我們要搜尋love you,我們只需要查詢包含每個詞條的文件:
Term | doc1 | doc2 |
---|---|---|
love | * | |
you | * |
搜尋是非常高效的,倒排索引根據詞條來排序,我們首先在詞條列表中打到love,然後掃描所有的列,可以快速看到doc2包含這個關鍵詞。
但聚合操作呢?我們需要找到doc2裡所有唯一的詞條,用倒排索引來完成,代價就非常高了,需要迭代索引的每個詞條,看一下有沒有doc2,有就把這個詞條收錄起來,沒有就檢查下一個詞條,直到整個倒排索引全部搜尋完成。很慢而且難以擴充套件,並且 會隨著資料量的增加而增加。
聚合查詢肯定不能用倒排索引了,那就用正排索引,建立的資料結構將變成這樣:
Doc | terms |
---|---|
doc1 | I, have, a, friend, who, loves, smile |
doc2 | love, me, I, you |
這樣的資料結構,我們要搜尋doc2包含多少個詞條就非常容易了。
倒排索引+正排索引結合的優勢
如果聚合查詢裡有帶過濾條件或檢索條件,先由倒排索引完成搜尋,確定文件範圍,再由正排索引提取field,最後做聚合計算。
這樣才是最高效的
幫助理解兩個索引結構
倒排索引,類似JAVA中Map的k-v結構,k是分詞後的關鍵詞,v是doc文件編號,檢索關鍵字特別容易,但要找到aggs的value值,必須全部搜尋v才能得到,效能比較低。
正排索引,也類似JAVA中Map的k-v結構,k是doc文件編號,v是doc文件內容,只要有doc編號作引數,提取相應的v即可,搜尋範圍小得多,效能比較高。
底層原理
基本原理
- 正排索引也是索引時生成(index-time),倒排索引也是index-time。
- 核心寫入原理與倒排索引類似,同樣基於不變原理設計,也寫os cache,磁碟等,os cache要存放所有的doc value,存不下時放磁碟。
- 效能問題,jvm記憶體少用點,os cache搞大一些,如64G記憶體的機器,jvm設定為16G,os cache記憶體給個32G左右,os cache夠大才能提升正排索引的快取和查詢效率。
column壓縮
正排索引本質上是一個序列化的連結串列,裡面的資料型別都是一致的(不一致說明索引建立不規範),壓縮時可以大大減少磁碟空間、提高訪問速度,如以下幾種壓縮技巧:
- 如果所有的數值各不相同(或缺失),設定一個標記並記錄這些值
- 如果這些值小於 256,將使用一個簡單的編碼表
- 如果這些值大於 256,檢測是否存在一個最大公約數
- 如果沒有存在最大公約數,從最小的數值開始,統一計算偏移量進行編碼
例如:
doc1: 550
doc2: 600
doc3: 500
最大公約數50,壓縮後的結果可能是這樣:
doc1: 11
doc2: 12
doc3: 10
同時最大公約數50也會儲存起來。
禁用正排索引
正排索引預設對所有欄位啟用,除了analyzed text。也就是說所有的數字、地理座標、日期和不分析(not_analyzed)字元型別都會預設開啟。針對某些欄位,可以不存正排索引,減少磁碟空間佔用(生產不建議使用,畢竟無法預知需求的變化),示例如下:
# 對欄位sessionId取消正排索引
PUT music
{
"mappings": {
"_doc": {
"properties": {
"sessionId": {
"type": "keyword",
"doc_values": false
}
}
}
}
}
同樣的,我們對倒排索引也可以取消,讓一個欄位可以被聚合,但是不能被正常檢索,示例如下:
PUT music
{
"mappings": {
"_doc": {
"properties": {
"sessionId": {
"type": "keyword",
"doc_values": true,
"index": false
}
}
}
}
}
fielddata原理
上一小節我們提到,正排索引對分詞的欄位是不啟用的,如果我們嘗試對一個分詞的欄位進行聚合操作,如music索引的author欄位,將得到如下提示:
Fielddata is disabled on text fields by default. Set fielddata=true on [author] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead.
這段提示告訴我們,如果分詞的欄位要支援聚合查詢,必須設定fielddata=true,然後把正排索引的資料載入到記憶體中,這會消耗大量的記憶體。
解決辦法:
- 設定fielddata=true
- 使用author.keyword欄位,建立mapping時有內建欄位的設定。
內部原理
analyzed字串的欄位,欄位分詞後佔用空間很大,正排索引不能很有效的表示多值字串,所以正排索引不支援此類欄位。
fielddata結構與正排索引類似,是另外一份資料,構建和管理100%在記憶體中,並常駐於JVM記憶體堆,極易引起OOM問題。
載入過程
fielddata載入到記憶體的過程是lazy載入的,對一個analzyed field執行聚合時,才會載入,而且是針對該索引下所有的文件進行field-level載入的,而不是匹配查詢條件的文件,這對JVM是極大的考驗。
fielddata是query-time建立,動態填充資料,而不是不是index-time建立,
記憶體限制
indices.fielddata.cache.size 控制為fielddata分配的堆空間大小。 當你發起一個查詢,分析字串的聚合將會被載入到fielddata,如果這些字串之前沒有被載入過。如果結果中fielddata大小超過了指定大小,其他的值將會被回收從而獲得空間(使用LRU演算法執行回收)。
預設無限制,限制記憶體使用,但是會導致頻繁evict和reload,大量IO效能損耗,以及記憶體碎片和gc,這個引數是一個安全衛士,必須要設定:
indices.fielddata.cache.size: 20%
監控fielddata記憶體使用
Elasticsearch提供了監控監控fielddata記憶體使用的命令,我們在上面可以看到記憶體使用和替換的次數,過高的evictions值(回收替換次數)預示著記憶體不夠用的問題和效能不佳的原因:
# 按索引使用 indices-stats API
GET /_stats/fielddata?fields=*
# 按節點使用 nodes-stats API
GET /_nodes/stats/indices/fielddata?fields=*
# 按索引節點
GET /_nodes/stats/indices/fielddata?level=indices&fields=*
fields=*表示所有的欄位,也可以指定具體的欄位名稱。
熔斷器
indices.fielddata.cache.size的作用範圍是當前查詢完成後,發現記憶體不夠用了才執行回收過程,如果當前查詢的資料比記憶體設定的fielddata 的總量還大,如果沒有做控制,可能就直接OOM了。
熔斷器的功能就是阻止OOM的現象發生,在執行查詢時,會預算記憶體要求,如果超過限制,直接掐斷請求,返回查詢失敗,這樣保護Elasticsearch不出現OOM錯誤。
常用的配置如下:
- indices.breaker.fielddata.limit:fielddata的記憶體限制,預設60%
- indices.breaker.request.limit:執行聚合的記憶體限制,預設40%
- indices.breaker.total.limit:綜合上面兩個,限制在70%以內
最好為熔斷器設定一個相對保守點的值。fielddata需要與request斷路器共享堆記憶體、索引緩衝記憶體和過濾器快取,並且熔斷器是根據總堆記憶體大小估算查詢大小的,而不是實際堆記憶體的使用情況,如果堆內有太多等待回收的fielddata,也有可能會導致OOM發生。
ngram對fielddata的影響
字首搜尋一章節我們介紹了ngram,ngram會生成大量的詞條,如果這個欄位同時設定fielddata=true的話,那麼會消耗大量的記憶體,這裡一定要謹慎。
fielddata精細化控制
fielddata過濾
過濾的主要目的是去掉長尾資料,我們可以加一些限制條件,如下請求:
PUT /music/_mapping/children
{
"properties": {
"tags": {
"type": "text",
"fielddata": true,
"fielddata_frequency_filter": {
"min": 0.001,
"max": 0.1,
"min_segment_size": 500
}
}
}
}
fielddata_frequency_filter過濾器會基於以下條件進行過濾:
- 出現頻率介紹0.1%和10%之間
- 忽略文件個數小於500的段檔案
fidelddata是按段來載入的,所以出現頻率是基於某個段計算得來的,如果一個段內只有少量文件,統計詞頻意義不大,等段合併到大的段當中,超過500個文件這個限制,就會納入計算。
fielddata資料對記憶體的佔用是顯而易見的,對fielddata過濾長尾是一種權衡。
序號標記預載入
假設我們的文件用來標記狀態有幾種字串:
- SUCCESS
- FAILED
- PENDING
- WAIT_PAY
狀態這類的欄位,系統設計時肯定是可以窮舉的,如果我們儲存到Elasticsearch中也用的是字串型別,需要的儲存空間就會多一些,如果我們換成1,2,3,4這種Byte型別的,就可以節省很多空間。
"序號標記"做的就是這種優化,如果文件特別多(PB級別),那節省的空間就非常可觀,我們可以對這類可以窮舉的欄位設定序號標記,如下請求:
PUT /music/_mapping/children
{
"properties": {
"tags": {
"type": "text",
"fielddata": true,
"eager_global_ordinals": true
}
}
}
深度優先VS廣度優先
Elasticsearch的聚合查詢時,如果資料量較多且涉及多個條件聚合,會產生大量的bucket,並且需要從這些bucket中挑出符合條件的,那該怎麼對這些bucket進行挑選是一個值得考慮的問題,挑選方式好,事半功倍,效率非常高,挑選方式不好,可能OOM,我們拿深度優先和廣度優先這兩個方式來講解。
我們舉個電影與演員的例子,一部電影由多名演員參與,我們搜尋的需求:出演電影最多的10名演員以及他們合作最多的5名演員。
如果是深度優先,示例圖如下:
這種查詢方式需要構建完整的資料,會消耗大量的記憶體。假設我們每部電影有10位演員(1主9配),有10萬部電影,那麼第一層的資料就有10萬條,第二層為9*10萬=90萬條,共100萬條資料。
我們對這100萬條資料進行排序後,取主角出演次數最多的10個,即10條資料,裁掉99加上與主角合作最多的5名演員,共50條資料。
構建了100萬條資料,最終只取50條,記憶體是不是有點浪費?
如果是廣度優先,示例圖如下:
這種查詢方式先查詢電影主角,取前面10條,第一層就只有10條資料,裁掉其他不要的,然後找出跟主角有關聯的配角人員,與合作最多的5名,共50條資料。
聚合查詢預設是深度優先,設定廣度優先只需要設定collect_mode引數為breadth_first,示例:
GET /music/children/_search
{
"size": 0,
"aggs": {
"lang": {
"terms": {
"field": "language",
"collect_mode" : "breadth_first"
},
"aggs": {
"length_avg": {
"avg": {
"field": "length"
}
}
}
}
}
}
注意
使用深度優先還是廣度優先,要考慮實際的情況,廣度優先僅適用於每個組的聚合數量遠遠小於當前總組數的情況,比如上面的例子,我只取10位主角,但每部電影都有一位主角,聚合的10位主角組數遠遠小於總組數,所以是適用的。
另外一組按月統計的柱狀圖資料,總組數固定只有12個月,但每個月下的資料量特別大,廣度優先就不適合了。
所以說,使用哪種方式要看具體的需求。
小結
本篇講解的聚合查詢原理,可以根據實際案例做一些演示,加深一下印象,多閱讀一下官網文件,實際工作中這塊用到的地方還是比較多的,謝謝。
專注Java高併發、分散式架構,更多技術乾貨分享與心得,請關注公眾號:Java架構社群
可以掃左邊二維碼新增好友,邀請你加入Java架構社群微信群共同探討技術
相關推薦
Elasticsearch系列---聚合查詢原理
概要 本篇主要介紹聚合查詢的內部原理,正排索引是如何建立的和優化的,fielddata的使用,最後簡單介紹了聚合分析時如何選用深度優先和廣度優先。 正排索引 聚合查詢的內部原理是什麼,Elastichsearch是用什麼樣的資料結構去執行聚合的?用倒排索引嗎? 工作原理 我們瞭解到倒排索引對搜尋是非常高效的,
ElasticSearch實戰系列五: ElasticSearch的聚合查詢基礎使用教程之度量(Metric)聚合
Title:ElasticSearch實戰系列四: ElasticSearch的聚合查詢基礎使用教程之度量(Metric)聚合 前言 在上上一篇中介紹了ElasticSearch實戰系列三: ElasticSearch的JAVA API使用教程,介紹了ElasticSearch Java API基礎的語法,基
ElasticSearch超強聚合查詢(一)
Elasticsearch聚合查詢一 聚合與搜尋的概念 通俗的說:搜尋是查詢某些具體的文件.然而聚合就是對這些搜尋到的文件進行統計.例如: (你的es資料裡面記錄的都是一些關於針的資料) 針的平均長度是多少? 按照針的製造商來分組,針的長度中位值是多少?
ElasticSearch教程-聚合查詢
分析 最後,我們還有一個需求需要完成:允許管理者在職員目錄中進行一些分析。 Elasticsearch有一個功能叫做聚合(aggregations),它允許你在資料上生成複雜的分析統計。它很像SQL中的GROUP BY但是功能更強大。 舉個例子,讓我們找到所有職員中最大的共同點(興趣愛好
Elasticsearch分組聚合-查詢
1 統計某個欄位下的分組情況 類似如下功能: select count(*) from mytable group by myfield curl -XPOST 'localhost:19200/ylchou-0-2015-10-07/_search?
elasticsearch多級聚合查詢
今天談談搜尋中經常用到的一個功能:聚合。我們知道elasticsearch作為當下流行的搜尋引擎當然是支援聚合的 一級聚合:統計每個分類的商品數目: "aggs": { "sample": {
Elasticsearch(8) --- 聚合查詢(Metric聚合)
Elasticsearch(8) --- 聚合查詢(Metric聚合) 在Mysql中,我們可以獲取一組資料的 最大值(Max)、最小值(Min)。同樣我們能夠對這組資料進行 分組(Group)。那麼對於Elasticsearch中 我們也可以實現同樣的功能,聚合有關資料官方文件內容較多,這裡大概分3篇或者
Elasticsearch(9) --- 聚合查詢(Bucket聚合)
Elasticsearch(9) --- 聚合查詢(Bucket聚合) 上一篇講了Elasticsearch聚合查詢中的Metric聚合:Elasticsearch(8) --- 聚合查詢(Metric聚合) 說明 本文主要參考於Elasticsearch 官方文件 7.3版本。 Bucket Aggreg
Elasticsearch學習筆記(三) 聚合查詢
聚合查詢 1. 準備資料 2. group by(舉例: 按照性別分組) 2.1 SQL描述 2.2 DSL描述 2.3 返回結果 3. avg(舉例: 求平均年齡) 3.1 SQL描述 3.2
ElasticSearch學習筆記之三十三 IK分詞器擴充套件字典及text全文型別資料分詞聚合查詢
ElasticSearch學習筆記之三十三 IK分詞器擴充套件字典及text全文型別資料分詞聚合查詢 專屬詞彙分詞失敗 擴充套件字典 檢視當前詞庫 自定義詞典 更新配置 再次檢視分詞 text全文型別資料分詞聚合
聚合查詢越來越慢?——詳解Elasticsearch的Global Ordinals與High Cardinality
Elasticsearch中的概念很多,本文將從筆者在實踐過程中遇到的問題出發,逐步詳細介紹 Global Ordinals 和 High Cardinality ,這也是筆者的認知過程。文中的Elasticsearch 版本為5.5。 背景 故事是這樣
基於Lucene查詢原理分析Elasticsearch的效能
摘要: 前言 Elasticsearch是一個很火的分散式搜尋系統,提供了非常強大而且易用的查詢和分析能力,包括全文索引、模糊查詢、多條件組合查詢、地理位置查詢等等,而且具有一定的分析聚合能力。因為其查詢場景非常豐富,所以如果泛泛的分析其查詢效能是一個非常複雜的事情,而且除了
Elasticsearch 時間分組聚合查詢
正常業務邏輯中,會出現大量的資料統計,比如說分組聚合查詢,根據天進行資料的統計,記錄下es分組聚合查詢 { "size": 0, "aggs": { "groupDate": { "date_histogram": {
[轉載]springboot中ElasticSearch入門與進階:組合查詢、Aggregation聚合查詢(你想要的都有)
原文:https://blog.csdn.net/topdandan/article/details/81436141 1.springboot中配置elasticSearch 1.1在工程中引入相關的jar包 1.1.1 在build.gradle中新增需要的jar包 我建立的gradle工
kibana對Elasticsearch做增刪改查操作,以及一些聚合查詢
kibana是一個視覺化平臺,設計出來用於和Elasticsearch一起使用的。你可以用kibana搜尋、檢視、互動存放在Elasticsearch索引裡的資料。 準備工作:Elasticsearch啟動完畢,elasticsearch-head啟動完畢 kibana 啟動完畢
MongoDB學習系列(二)mongo聚合查詢例項
mongoDB聚合查詢Java程式碼,小例項。 private void init(String year) throws Exception { if (year == null) { year = new SimpleDateFo
Elasticsearch 聚合查詢及複合查詢
集合查詢 sum聚合 sum是一個求累加值的聚合,其作用與關係型資料庫中相同。 GET /lib4/items/_search { "size": 0, //表示查詢多少條文件,聚合只需就和結果,輸出文件可以設定為0條 "aggs"
ElasticSearch教程——聚合資料內部原理以及doc value機制核心級原理
聚合資料內部原理 聚合分析的內部原理是什麼????aggs,term,metric avg max,執行一個聚合操作的時候,內部原理是怎樣的呢?用了什麼樣的資料結構去執行聚合?是不是用的倒排索引? 搜尋+聚合,寫個示例 GET /test_index/test_
Elasticsearch聚合查詢
將文字field的fielddata屬性設定為true PUT /ecommerce/_mapping/product { "properties": { "tags": { "type": "text", "fielddata": true } } } GET
elasticsearch系列-ES對多個欄位聚合,select A,B,COUNT(*) from table group by A,B
ES對多個欄位聚合,select A,B,COUNT(*)from table group by A,B 假設有下表 NAME SEX PROF 李誠 男 副教授 張旭 男 講師 王萍 女 助教 劉冰