Elasticsearch 聚合分析詳解

Elasticsearch 聚合分析詳解

聚合分析Aggregations

聚合分析:英文為Aggregation,是es除搜尋功能外提供的針對es資料做統計分析的功能。

  • 功能豐富:提供Bucket、Metric、Pipeline等多種分析方式,可以滿足大部分的分析需求
  • 實時性高:所有的計算結果都是即時返回的,而hadoop等大資料系統一般都是T 1級別的

例如聚合分析可以回答如下問題:

  • 請告訴我最近1周每天的訂單成交量有多少?
  • 請告訴我最近1個月每天的平均訂單金額是多少?
  • 請告訴我最近半年賣的最火的前5個商品有哪些?

聚合分析分類:為了便於理解,es將聚合分析主要分為如下4類:

  • Bucket:分桶型別,類似SQL中的GROUP BY語法
  • Metric:指標分析型別,如計算最大值、最小值、平均值等等
  • Pipeline:管道分析型別,基於上一級的聚合分析結果進行在分析
  • Matrix:矩陣分析型別

Metric聚合分析

Metric聚合分析分為單值分析和多值分析兩類:

  • 單值分析,只輸出一個分析結果

    min,max,avg,sum
    cardinality
    
  • 多值分析,輸出多個分析結果

    stats,extended stats
    percentile,percentile rank
    top hits 
    

min,max,avg,sum

返回數值欄位的最小值:

GET myindex/_search
{
"size": 0,   #不返回文件列表
"aggs": {
"min_age": {
"min": {    #關鍵詞
"field": "age"
}
}
}
}           

注意我們將size設定成0,這樣我們就可以只看到聚合結果了,而不會顯示命中的結果

返回數值欄位的最大值/平均值/求和只需要將關鍵詞min替換對應的為max/avg/sum即可。

一次返回多個聚合結果 (並列關係,不是子聚合)

GET myindex/_search
{
"size": 0,
"aggs": {
"min_age": {
"min": {
"field": "age"
}
},
"avg_age":{
"avg":{
"field":"age"
}
},
"max_age":{
"max":{
"field":"age"
}
}
}
}   

這裡寫圖片描述

cardinality

cardinality:意為集合的勢,或者基數,是指不同數值的個數,類似SQL中的distinct count概念。
例如查詢性別(M|F)的基數,返回的結果為2

GET /bank/account/_search
{
"size":0,
"aggs":{
"count_of_genders":{
"cardinality": {
"field": "gender.keyword"
}
}
}
}

stats,extended stats

stats:返回一系列數值型別的統計值,包含min、max、avg、sum和count

GET bank/account/_search
{
"size": 0,
"aggs": {
"stats_age": {
"stats": {
"field": "age"
}
}
}
}

這裡寫圖片描述

extended stats:對stats的擴充套件,包含了更多的統計資料,比如方差、標準差等
這裡寫圖片描述

Percentile,Percentile Rank

Percentile: 百分位數統計

GET bank/account/_search
{
"size": 0,
"aggs": {
"per_age": {
"percentiles": {
"field": "age",
"percents": [
1,
5,
25,
50,
75,
95,
99
]
}
}
}
}

這裡寫圖片描述
Percentile Rank: 百分位數統計

GET bank/account/_search
{
"size": 0,
"aggs": {
"per_age": {
"percentile_ranks": {
"field": "age",
"values": [
20,
35,
40
]
}
}
}
}       

這裡寫圖片描述

Top Hits

Top Hits: 一般用於分桶後獲取該桶內匹配的頂部文件列表,即詳情資料
例如,按照性別進行分組,並對每組中按照balance進行排序(子聚合)

GET bank/account/_search
{
"size": 0,
"aggs": {
"group_by_gender": {
"terms": {
"field": "gender.keyword"
},
"aggs": {
"top_employee": {
"top_hits": {
"size": 2,
"_source": ["gender","balance"], 
"sort": [
{
"balance": {
"order": "desc"
}
}
]
}
}
}
}
}
}

這裡寫圖片描述

Bucket聚合分析

Bucket:意為桶,即按照一定的規則將文件分配到不同的桶中,達到分類分析的目的
這裡寫圖片描述

按照Bucket的分桶策略,常見的Bucket聚合分析如下:

  • Terms
  • Range
  • Date Range
  • Histogram
  • Date Histogram

Terms

Terms: 最簡單的分桶策略,直接按照term來分桶,如果是text型別,則按照分詞後的結果分桶

GET /bank/account/_search
{
"size":0,
"aggs": {
"group_by_gender": {
"terms": {
"field": "gender.keyword"
}
}
}
}   

這裡寫圖片描述

Range,Date Range

Range: 通過制定數值的範圍來設定分桶規則

GET  /bank/account/_search
{
"size": 0,
"aggs": {
"range_age": {
"range": {
"field": "age",
"ranges": [
{
"to": 25
},
{
"from": 25,
"to": 35
},
{
"from": 35
}
]
}
}
}
}   

這裡寫圖片描述
Date Range: 通過指定日期的範圍來設定分桶規則

GET myindex/_search
{
"size": 0,
"aggs": {
"data_range": {
"date_range": {
"field": "",
"format": "MM-yyy", 
"ranges": [
{
"from": "now-10d/d",
"to": "now"
}
]
}
}
}
}

Historgram,Date Histogram

Historgram: 直方圖,以固定間隔的策略來分割資料

GET bank/account/_search
{
"size": 0,
"aggs": {
"hist_age": {
"histogram": {
"field": "age",
"interval": 10,
"extended_bounds":{
"min":10,
"max":50
}
}
}
}
}   

這裡寫圖片描述

Date Histogram: 針對日期的直方圖或者柱狀圖,是時序分析中常用的聚合分析型別

GET myindex/_search
{
"size": 0,
"aggs": {
"by_year": {
"date_histogram": {
"field": "date",
"interval": "month"
, "format": "yyyy-MM-dd"
}
}
}
}

Bucket Metric聚合分析

Bucket聚合分析允許通過子分析來進一步進行分析,該分析可以是Bucket也可以是Metric,這也使得es的聚合分析能力變得異常強大。
(1)分桶之後在分桶

GET /bank/account/_search
{
"size": 0,
"aggs": {
"group_by_gender": {
"terms": {
"field": "gender.keyword"
},
"aggs": {
"range_age": {
"range": {
"field": "age",
"ranges": [
{
"from": 20,
"to": 30
},
{
"from":30,
"to":40
}
]
}
}
}
}
}
}   

這裡寫圖片描述

(2)分桶後進行資料分析

GET /bank/account/_search
{
"size":0,
"aggs": {
"group_by_gender": {
"terms": {
"field": "gender.keyword"
},
"aggs": {
"stats_age": {
"stats": {
"field": "age"
}
}
}
}
}
}

這裡寫圖片描述

Pipeline 聚合分析

針對聚合分析的結果再次進行聚合分析,而且支援鏈式呼叫,可以回答如下問題:訂單月平均銷售額是多少?
這裡寫圖片描述

Pipeline的分析結果會輸出到原有結果彙總,根據輸出位置的不同,分為以下兩類:

  • Parent結果內嵌到現有聚合分析結果中

        Derivative      導數求導
    Moving Average  移動平均
    Cumulative Sum  累計求和
    
  • Sibling結果與現有聚合分析結果同級

        Max/Min/Avg/Sum Bucket
    Stats/Extended Stats Bucket
    Percentiles Bucket
    

Sibling – Min Bucket

Sibling - Min Bucket : 找出所有bucket中值最小的bucket名稱和值

GET /bank/account/_search
{
"size": 0,
"aggs": {
"group_by_stats": {
"terms": {
"field": "state.keyword"
},
"aggs": {
"average_balance": {
"avg": {
"field": "balance"
}
}
}
},
"min_avg_by_balance":{
"min_bucket": {
"buckets_path": "group_by_stats>average_balance"
}
}
}
}     

這裡寫圖片描述

對應的還有:max_bucket/avg_bucket/sum_buctet/stats_bucket等等

Parent – Derivative

Parent - Derivative: 計算Bucket值的導數

“derivative aggregation [derivative_by_average_balance] must have a histogram or date_histogram as parent”

GET bank/account/_search
{
"size":0,
"aggs": {
"hist_city": {
"histogram": {
"field": "age",
"interval": 5
},
"aggs":{
"avg_balance":{
"avg": {
"field": "balance"
}
},
"derivative_avg_balance":{
"derivative": {
"buckets_path": "avg_balance"
}
}
}
}
}
}

這裡寫圖片描述

Moving Average移動平均(moving_avg)、Cumulative Sum累計求和(cumulative_sum)的用法同derivative

聚合分析作用範圍

es聚合分析預設作業範圍是query的結果集,可以通過如下的方式改變其作業範圍:

  • filter: 為某個聚合分析設定過濾條件,從而在不更改整體query語句的情況下修改了作用範圍
  • Post_filter: 作用於文件過濾,但在聚合分析後生效
  • global:無視query過濾條件,基於全部文件進行分析

聚合分析中的排序

(一)可以使用自帶的關鍵資料進行排序,比如:

  • _count 文件數
  • _key 按照key值排序
GET bank/account/_search
{
"size":0,
"aggs": {
"group_by_state": {
"terms": {
"field": "state.keyword",
"size": 5,
"order": {
"_count": "desc"
}
}
}
}
}

(二)根據子聚合排序

GET /bank/account/_search
{
"size": 0,
"aggs": {
"group_by_state": {
"terms": {
"field": "state.keyword",
"size": 10,
"order": {
"avg_balance": "asc"
}
},
"aggs": {
"avg_balance": {
"avg": {
"field": "balance"
}
}
}
}
}
}
GET /bank/account/_search
{
"size": 0,
"aggs": {
"group_by_state": {
"terms": {
"field": "state.keyword",
"size": 10,
"order": {
"stats_balance.avg": "asc"
}
},
"aggs": {
"stats_balance": {
"stats": {
"field": "balance"
}
}
}
}
}
}

聚合分析精準度問題

這裡寫圖片描述
Terms不準確的解決方法:

  • 設定Shard數為1,消除資料分散的問題,但無法承載大資料量
  • 合理設定Shard_Size大小,即每次從Shard上額外多獲取資料,以提升準確度
GET bank/account/_search
{
"size": 0,
"aggs": {
"group_by_state": {
"terms": {
"field": "state.keyword",
"size": 2,
"shard_size": 10
}
}
}
}       

Shard_Size大小的設定方法:

terms聚合返回結果有如下兩個統計值:

doc_count_error_upper_bound  被遺漏的term可能的最大值
sum_other_doc_count  返回結果bucket的term外其他term的文件總數

設定show_term_doc_count_error可以檢視每個bucket誤算的最大值:

GET bank/account/_search
{
"size": 0,
"aggs": {
"group_by_state": {
"terms": {
"field": "state.keyword",
"size": 2,
"show_term_doc_count_error": true
}
}
}
}           

返回結果中"doc_count_error_upper_bound": 0 : 0表示計算準確

Shard_Size預設大小如下:

shard_size=(size*1.5) 10

通過調整Shard_size的大小降低doc_count_error_upper_bound來提升準確度,增大了整體的計算量,從而降低了響應時間

近似統計演算法:

這裡寫圖片描述

在es聚合分析中,CardinalityPercentile分析使用的是近似統計演算法:

  • 結果是近似準確的,但不一定精準
  • 可以通過引數的調整使其結果精準,但同時也意味著更多的計算時間和更大的效能消耗

執行聚合示例

聚合提供了分組並統計資料的能力。理解聚合的最簡單的方式是將其粗略地等同為SQL的GROUP BY和SQL聚合函式。在Elasticsearch中,你可以在一個響應中同時返回命中的資料和聚合結果。你可以使用簡單的API同時執行查詢和多個聚合,並以一次返回,這避免了來回的網路通訊,這是非常強大和高效的。

首先,本示例按狀態對所有帳戶進行分組,然後返回按降序(也是預設值)排序的前10個(預設)狀態:

curl -H "Content-Type: application/json" -XPOST '192.168.20.60:9200/bank/_search?pretty' -d '
{
"size": 0,
"aggs": {
"group_by_state": {
"terms": {
"field": "state.keyword"
}
}
}
}'  

在SQL中,上面的聚合在概念上類似於:

SELECT state, COUNT(*) FROM bank GROUP BY state ORDER BY COUNT(*) DESC

部分結果顯示:

{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 1000,
"max_score" : 0.0,
"hits" : [ ]
},
"aggregations" : {
"group_by_state" : {
"doc_count_error_upper_bound" : 20,
"sum_other_doc_count" : 770,
"buckets" : [
{
"key" : "ID",
"doc_count" : 27
},
{
"key" : "TX",
"doc_count" : 27
},
{
"key" : "AL",
"doc_count" : 25
},
{
"key" : "MD",
"doc_count" : 25
},
{
"key" : "TN",
"doc_count" : 23
},              

我們可以看到AL(abama)有25個賬戶,TX有27個賬戶,ID(daho)有27個賬戶

注意我們將size設定成0,這樣我們就可以只看到聚合結果了,而不會顯示命中的結果

在先前聚合的基礎上,現在這個例子計算了每個州的賬戶的平均餘額(還是按照賬戶數量倒序排序的前10個州):

curl -H "Content-Type: application/json" -XPOST '192.168.20.60:9200/bank/_search?pretty' -d '
{
"size": 0,
"aggs": {
"group_by_state": {
"terms": {
"field": "state.keyword"
},
"aggs": {
"average_balance": {
"avg": {
"field": "balance"
}
}
}
}
}
}'

注意,我們把average_balance聚合巢狀在了group_by_state聚合之中。這是所有聚合的一個常用模式。你可以任意的聚合之中巢狀聚合,這樣你就可以從你的資料中抽取出想要的概述。

基於前面的聚合,現在讓我們按照平均餘額進行排序:

curl -H "Content-Type: application/json" -XPOST '192.168.20.60:9200/bank/_search?pretty' -d '
{
"size": 0,
"aggs": {
"group_by_state": {
"terms": {
"field": "state.keyword",
"order": {
"average_balance": "desc"
}
},
"aggs": {
"average_balance": {
"avg": {
"field": "balance"
}
}
}
}
}
}'  

下面的例子顯示瞭如何使用年齡段(20-29,30-39,40-49)分組,然後在用性別分組,然後為每一個年齡段的每一個性別計算平均賬戶餘額:

curl -H "Content-Type: application/json" -XPOST '192.168.20.60:9200/bank/_search?pretty' -d '
{
"size": 0,
"aggs": {
"group_by_age": {
"range": {
"field": "age",
"ranges": [
{
"from": 20,
"to": 30
},
{
"from": 30,
"to": 40
},
{
"from": 40,
"to": 50
}
]
},
"aggs": {
"group_by_gender": {
"terms": {
"field": "gender.keyword"
},
"aggs": {
"average_balance": {
"avg": {
"field": "balance"
}
}
}
}
}
}
}
}'