公號:碼農充電站pro
主頁:https://codeshellme.github.iohtml
ES 中的聚合分析(Aggregations)是對數據的統計分析功能,它的優勢是實時性較高,相比於 Hadoop 速度更快。git
ES 中的聚合分析主要有如下 3 大類,每一類都提供了多種統計方法:github
通常使用聚合分析時,一般將 size 設置爲 0,表示不須要返回查詢結果,只須要返回聚合結果。算法
一個示例:shell
# 多個 Metric 聚合,找到最低最高和平均工資 POST index_name/_search { "size": 0, # size 爲 0 "aggs": { "max_salary": { # 自定義聚合名稱 "max": { # 聚合類型 "field": "salary" # 聚合字段 } }, "min_salary": { # 自定義聚合名稱 "min": { # 聚合類型 "field": "salary" # 聚合字段 } }, "avg_salary": { # 自定義聚合名稱 "avg": { # 聚合類型 "field": "salary" # 聚合字段 } } } }
Metrics 聚合能夠分爲單值分析和多值分析:數組
示例,一個員工表定義:性能優化
DELETE /employees PUT /employees/ { "mappings" : { "properties" : { "age" : { "type" : "integer" }, "gender" : { "type" : "keyword" }, "job" : { "type" : "text", "fields" : { "keyword" : { # 子字段名稱 "type" : "keyword", # 子字段類型 "ignore_above" : 50 } } }, "name" : { "type" : "keyword" }, "salary" : { "type" : "integer" } } } }
插入一些測試數據:app
PUT /employees/_bulk { "index" : { "_id" : "1" } } { "name" : "Emma","age":32,"job":"Product Manager","gender":"female","salary":35000 } { "index" : { "_id" : "2" } } { "name" : "Underwood","age":41,"job":"Dev Manager","gender":"male","salary": 50000} { "index" : { "_id" : "3" } } { "name" : "Tran","age":25,"job":"Web Designer","gender":"male","salary":18000 } { "index" : { "_id" : "4" } } { "name" : "Rivera","age":26,"job":"Web Designer","gender":"female","salary": 22000} { "index" : { "_id" : "5" } } { "name" : "Rose","age":25,"job":"QA","gender":"female","salary":18000 } { "index" : { "_id" : "6" } } { "name" : "Lucy","age":31,"job":"QA","gender":"female","salary": 25000} { "index" : { "_id" : "7" } } { "name" : "Byrd","age":27,"job":"QA","gender":"male","salary":20000 } { "index" : { "_id" : "8" } } { "name" : "Foster","age":27,"job":"Java Programmer","gender":"male","salary": 20000} { "index" : { "_id" : "9" } } { "name" : "Gregory","age":32,"job":"Java Programmer","gender":"male","salary":22000 } { "index" : { "_id" : "10" } } { "name" : "Bryant","age":20,"job":"Java Programmer","gender":"male","salary": 9000} { "index" : { "_id" : "11" } } { "name" : "Jenny","age":36,"job":"Java Programmer","gender":"female","salary":38000 } { "index" : { "_id" : "12" } } { "name" : "Mcdonald","age":31,"job":"Java Programmer","gender":"male","salary": 32000} { "index" : { "_id" : "13" } } { "name" : "Jonthna","age":30,"job":"Java Programmer","gender":"female","salary":30000 } { "index" : { "_id" : "14" } } { "name" : "Marshall","age":32,"job":"Javascript Programmer","gender":"male","salary": 25000} { "index" : { "_id" : "15" } } { "name" : "King","age":33,"job":"Java Programmer","gender":"male","salary":28000 } { "index" : { "_id" : "16" } } { "name" : "Mccarthy","age":21,"job":"Javascript Programmer","gender":"male","salary": 16000} { "index" : { "_id" : "17" } } { "name" : "Goodwin","age":25,"job":"Javascript Programmer","gender":"male","salary": 16000} { "index" : { "_id" : "18" } } { "name" : "Catherine","age":29,"job":"Javascript Programmer","gender":"female","salary": 20000} { "index" : { "_id" : "19" } } { "name" : "Boone","age":30,"job":"DBA","gender":"male","salary": 30000} { "index" : { "_id" : "20" } } { "name" : "Kathy","age":29,"job":"DBA","gender":"female","salary": 20000}
min 聚合分析:electron
# Metric 聚合,找到最低的工資 POST employees/_search { "size": 0, "aggs": { "min_salary": { "min": { # 聚合類型,求最小值 "field":"salary" } } } } # 返回結果 "hits": { "total": { "value": 20, # 一共統計了多少條數據 "relation": "eq" }, "max_score": null, "hits": [...] # 由於 size 爲 0 }, "aggregations": { "min_salary": { # 自定義的聚合名稱 "value": 9000, } }
stats 聚合分析:elasticsearch
# 輸出多值 POST employees/_search { "size": 0, "aggs": { "stats_salary": { "stats": { # stats 聚合 "field":"salary" } } } } # 返回多值結果 "aggregations": { "stats_salary": { # 自定義的聚合名稱 "count": 20, "min": 9000, "max": 50000, "avg": 24700, "sum": 494000 } }
# 指定 size,不一樣崗位中,年紀最大的3個員工的信息 POST employees/_search { "size": 0, "aggs":{ "old_employee":{ # 聚合名稱 "top_hits":{ # top_hits 分桶 "size":3, "sort":[ # 根據 age 倒序排序,選前 3 個 {"age":{"order":"desc"}} ] } } } }
Bucket 聚合按照必定的規則,將文檔分配到不一樣的桶中,達到分類的目的。
Bucket 聚合支持嵌套,也就是在桶裏再次分桶。
Bucket 聚合算法:
示例:
# 對 keword 進行聚合 POST employees/_search { "size": 0, # size 爲 0 "aggs": { "jobs": { # 自定義聚合名稱 "terms": { # terms 聚合 "field":"job.keyword" # job 字段的 keyword 子字段 } } } } # 返回值結構示例 "aggregations": { "genres": { "doc_count_error_upper_bound": 0, "sum_other_doc_count": 0, "buckets": [ # 不少桶,這是一個數組 { "key": "electronic", "doc_count": 6 }, { "key": "rock", "doc_count": 3 }, { "key": "jazz", "doc_count": 2 } ] } }
對 Text 字段進行 terms 聚合查詢會出錯,示例:
# 對 Text 字段進行 terms 聚合查詢 POST employees/_search { "size": 0, "aggs": { "jobs": { "terms": { "field":"job" # job 是 text 類型 } } } } # 對 Text 字段打開 fielddata,以支持 terms aggregation PUT employees/_mapping { "properties" : { "job":{ "type": "text", "fielddata": true # 打開 fielddata } } }
當某個字段的寫入和 Terms 聚合比較頻繁的時候,可用經過打開 eager_global_ordinals 配置來對 Terms 操做進行優化。
示例:
PUT index_name { "mappings": { "properties": { "foo": { # 字段名稱 "type": "keyword", "eager_global_ordinals": true # 打開 } } } }
Bucket 聚合支持添加子聚合來進一步分析,子聚合能夠是一個 Metrics 或者 Bucket。
示例 1:
# 指定 size,不一樣崗位中,年紀最大的3個員工的信息 POST employees/_search { "size": 0, "aggs": { "jobs": { "terms": { # 先作了一個 terms 分桶 "field":"job.keyword" }, "aggs":{ # 嵌套一個聚合,稱爲子聚合, "old_employee":{ # 聚合名稱 "top_hits":{ # top_hits 分桶 "size":3, "sort":[ # 根據 age 倒序排序,選前 3 個 {"age":{"order":"desc"}} ] } } } } } }
示例 2 :
POST employees/_search { "size": 0, "aggs": { "Job_salary_stats": { "terms": { # 先作了一個 terms 分桶 "field": "job.keyword" }, "aggs": { "salary": { "stats": { # 子聚合是一個 stats "field": "salary" } } } } } } # 屢次嵌套 POST employees/_search { "size": 0, "aggs": { # 第 1 層 "Job_gender_stats": { "terms": { "field": "job.keyword" # 先根據崗位分桶 }, "aggs": { # 第 2 層 "gender_stats": { "terms": { "field": "gender" # 再根據性別分桶 }, "aggs": { # 第 3 層 "salary_stats": { "stats": { # 最後根據工資統計 stats "field": "salary" } } } } } } } }
對員工的工資進行區間聚合:
# Salary Ranges 分桶,能夠本身定義 key POST employees/_search { "size": 0, "aggs": { "salary_range": { # 自定義聚合名稱 "range": { # range 聚合 "field":"salary", # 聚合的字段 "ranges":[ # range 聚合規則/條件 { "to":10000 # salary < 10000 }, { "from":10000, # 10000 < salary < 20000 "to":20000 }, { # 若是沒有定義 key,ES 會自動生成 "key":"可使用 key 自定義名稱", "from":20000 # salary > 20000 } ] } } } }
示例,工資0到10萬,以 5000一個區間進行分桶:
# Salary Histogram POST employees/_search { "size": 0, "aggs": { "salary_histrogram": { # 自定義聚合名稱 "histogram": { # histogram 聚合 "field":"salary", # 聚合的字段 "interval":5000, # 區間值 "extended_bounds":{ # 範圍 "min":0, "max":100000 } } } } }
Pipeline 聚合用於對其它聚合的結果進行再聚合。
根據 Pipeline 聚合與原聚合的位置區別,分爲兩類:
Max_bucket
,Min_bucket
,Avg_bucket
,Sum_bucket
Stats_bucket
,Extended-Status_bucket
Percentiles_bucket
Derivative
:求導Cumulative-sum
:累計求和Moving-function
:滑動窗口示例:
# 平均工資最低的工做類型 POST employees/_search { "size": 0, "aggs": { "jobs": { # 自定義聚合名稱 "terms": { "field": "job.keyword", # 先對崗位類型進行分桶 "size": 10 }, "aggs": { "avg_salary": { "avg": { "field": "salary" # 再計算每種工資崗位的平價值 } } } }, "min_salary_by_job":{ # 自定義聚合名稱 "min_bucket": { # pipeline 聚合 "buckets_path": "jobs>avg_salary" } # 含義是:對 jobs 中的 avg_salary 進行一個 min_bucket 聚合 } } }
示例:
# 示例 1 POST employees/_search { "size": 0, "aggs": { "age": { # 自定義聚合名稱 "histogram": { "field": "age", "min_doc_count": 1, "interval": 1 }, "aggs": { "avg_salary": { # 自定義聚合名稱 "avg": { "field": "salary" } }, # 自定義聚合名稱 "derivative_avg_salary":{ # 注意 derivative 聚合的位置,與 avg_salary 同級 "derivative": { # 而不是與 age 同級 "buckets_path": "avg_salary" # 注意這裏再也不有箭頭 > } } } } } } # 示例 2 POST employees/_search { "size": 0, "aggs": { "age": { "histogram": { "field": "age", "min_doc_count": 1, "interval": 1 }, "aggs": { "avg_salary": { "avg": { "field": "salary" } }, "cumulative_salary":{ "cumulative_sum": { # 累計求和 "buckets_path": "avg_salary" } } } } } }
ES 聚合的默認做用範圍是 Query 的查詢結果,若是沒有寫 Query,那默認就是在索引的全部數據上作聚合。
好比:
POST employees/_search { "size": 0, "query": { # 在 query 的結果之上作聚合 "range": { "age": {"gte": 20} } }, "aggs": { "jobs": { "terms": {"field":"job.keyword"} } } }
ES 支持經過如下方式來改變聚合的做用範圍:
示例:
POST employees/_search { "size": 0, "aggs": { "older_person": { # 自定義聚合名稱 "filter":{ # 經過 filter 改變聚合的做用範圍 "range":{ "age":{"from":35} } }, # end older_person "aggs":{ # 在 filter 的結果之上作聚合 "jobs":{ # 自定義聚合名稱 "terms": {"field":"job.keyword"} } } }, # end older_person "all_jobs": { # 又一個聚合,沒有 filter "terms": {"field":"job.keyword"} } } }
示例:
POST employees/_search { "aggs": { "jobs": { # 自定義聚合名稱 "terms": {"field": "job.keyword"} } }, # end aggs "post_filter": { # 一個 post_filter,對聚合的結果進行過濾 "match": { "job.keyword": "Dev Manager" } } }
POST employees/_search { "size": 0, "query": { # 一個 query "range": { "age": {"gte": 40} } }, "aggs": { "jobs": { # 一個聚合 "terms": {"field":"job.keyword"} }, "all":{ # 又一個聚合,名稱爲 all "global":{}, # 這裏的 global 會覆蓋掉上面的 query,使得聚合 all 的做用範圍不受 query 的影響 "aggs":{ # 子聚合 "salary_avg":{ # 自定義聚合名稱 "avg":{"field":"salary"} } } } } }
聚合中的排序使用 order 字段,默認按照 _count 和 _key 進行排序。
_count
:表示按照文檔數排序,若是不指定 _count,默認按照降序進行排序。_key
:表示關鍵字(字符串值),若是文檔數相同,再按照 key 進行排序。示例 1:
# 使用 count 和 key POST employees/_search { "size": 0, "query": { "range": { "age": {"gte": 20} } }, "aggs": { "jobs": { # 自定義聚合名稱 "terms": { # terms 聚合 "field":"job.keyword", "order":[ # order 排序 {"_count":"asc"}, # 先安裝文檔數排序 {"_key":"desc"} # 若是文檔數相同,再按照 key 排序 ] } } } }
也能夠基於子聚合排序。
示例 2:
# 先對工做種類進行分桶 # 再以工做種類的平均工資進行排序 POST employees/_search { "size": 0, "aggs": { "jobs": { # 自定義聚合名稱 "terms": { "field":"job.keyword", "order":[ # 基於子聚合的排序 {"avg_salary":"desc"} ] }, # end terms "aggs": { # 子聚合 "avg_salary": { # 子聚合名稱 "avg": {"field":"salary"} } } } # end jobs } }
若是子聚合是多值輸出,也能夠基於 子聚合名.屬性
來進行排序,以下:
POST employees/_search { "size": 0, "aggs": { "jobs": { "terms": { "field":"job.keyword", "order":[ # 基於子聚合的屬性排序 {"stats_salary.min":"desc"} ] }, # end terms "aggs": { "stats_salary": { # 子聚合是多值輸出 "stats": {"field":"salary"} } } } # end jobs } }
下面介紹聚合分析的原理及精準度的問題。
分佈式系統中有三個概念:
對於分佈式系統(數據分佈在不一樣的分片上),這三個指標不能同時具有,同時只能知足其中的 2 個條件:
ES 屬於近似計算,具有了數據量和實時性的特色,失去了精準度。
ES 是一個分佈式系統,數據分佈在不一樣的分片上。
所以,ES 在進行聚合分析時,會先在每一個主分片上作聚合,而後再將每一個主分片上的聚合結果進行彙總,從而獲得最終的聚合結果。
分佈式聚合的原理,會天生帶來精準度的問題,但並非全部的聚合分析都有精準度問題:
下面來看下 Terms 聚合存在的問題,下圖中的:
下圖是 Terms 聚合流程:
上圖中,在進行 Terms 聚合時(最終結果只要按照數量排序的前 3 個),須要分別在分片 P0 和 P1上作聚合,而後再將它們的聚合結果進行彙總。
正確的聚合結果應該是 A(12),B(6),D(6)
,可是因爲分片的緣由,ES 計算出來的結果是 A(12),B(6),C(4)
。這就是 Terms 聚合存在的精準度問題。
打開 show_term_doc_count_error 配置可使得 terms 聚合的返回結果中有一個 doc_count_error_upper_bound
值(最小爲0),經過該值能夠了解精準程度;該值越小,說明 Terms 的精準度越高。
POST index_name/_search { "size": 0, "aggs": { "weather": { # 自定義聚合名稱 "terms": { # terms 聚合 "field":"OriginWeather", "show_term_doc_count_error":true # 打開 } } } }
提升 terms 聚合的精準度有兩種方式:
設置 shard_size 的語法:
POST my_flights/_search { "size": 0, "aggs": { "weather": { "terms": { "field":"OriginWeather", "size":1, "shard_size":1, "show_term_doc_count_error":true } } } }
(本節完。)
推薦閱讀:
歡迎關注做者公衆號,獲取更多技術乾貨。