一、前言
聚合是一種基於搜尋的資料彙總,通過組合可以完成複雜的操作。聚合可以對文件進行彙總、分組等。通過聚合,我們會得到一個資料的概覽,是分析和總結全部的資料,而不是尋找單個文件。
二、分類
- Bucket Aggregation:一些列滿足特定條件的文件的集合,類似MySQL的“group by”
- Metric Aggregation:一些數學運算,可以對文件欄位進行統計分析,比如max、min、sum等。
- Pipeline Aggregation:對其他的聚合結果進行二次聚合
- Matrix Aggregation:支援對多個欄位的操作並提供一個結果矩陣,7.x版本合併到Metric Aggregation中了。
三、聚合結構
{
"size": 0,
["query": {}, ]?
"aggs" : {
"${my_name}" : {
"${aggregation_type}" : {
<aggregation_body>
}
[,"meta" : { [<meta_data_body>] } ]?
[,"aggs" : { [<sub_aggregation>]+ } ]?
}
[,"${my_name}" : { ... } ]*
}
}
- 聚合可以進行巢狀,比如上面的“aggs”內部又巢狀了一個“aggs”
- “aggs”是簡寫,也可以寫完整“aggregations”
- 最上面的“size”一般設定為0,聚合操作用於統計資料,無需輸出文件
- query 查詢,可選
- my_name 自定義名字
四、測試資料
使用kibana匯入“kibana_sample_data_flights”,這個是飛機的航班資訊,有地區、價格、天氣等資訊。
操作路徑:Home --> 新增資料 --> 樣例資料 --> Sample flight data
五、Bucket Aggregation
1、子聚合
根據目的地(DestCountry)進行分組,檢視航班的數量
GET kibana_sample_data_flights/_search
{
"size": 0,
"aggs": {
"dest_count": {
"terms": {
"field": "DestCountry"
}
}
}
}
2、數字區間分組
根據價格區間進行分組,比如0到100元多少個,100到200元多少個
GET kibana_sample_data_flights/_search
{
"size": 0,
"aggs": {
"price_stat": { // 自定義名字
"histogram": {
"field": "AvgTicketPrice",
"interval": 100 // 指定區間
}
}
}
}
輸出的結果中,key為“100.0”代表0到100.0的資料,計算公式如下
bucket_key = Math.floor(value / interval) * interval
3、日期區間分組
GET kibana_sample_data_flights/_search
{
"size": 0,
"aggs": {
"price_stat": {
"date_histogram": {
"field": "timestamp",
"calendar_interval": "month"
}
}
}
}
注意:日期間隔設定,7.x版本用“calendar_interval”,老版本用“interval”。
支援的時間間隔表示式
- 分鐘:minute, 1m
- 小時:hour, 1h
- 天:day, 1d
- 星期:week, 1w
- 月:month, 1M
- 季度:quarter, 1q
- 年:year, 1y
六、Metric Aggregation
計算度量這類的聚合操作是以使用一種方式或者從文件中提取需要聚合的值為基礎的。這些資料不但可以從文件(使用資料屬性)的屬性中提取出來,也可以使用指令碼生成。
支援max、min、count、sum、avg、stats(各種統計資訊)、cardinality(去重後數量)、percentiles(百分位)、geo_bounds(地理邊界)
1、最值
輸出航班的最大價格,最小价格
GET kibana_sample_data_flights/_search
{
"size": 0,
"aggs": {
"max_price": {
"max": {
"field": "AvgTicketPrice"
}
},
"mix_price": {
"min": {
"field": "AvgTicketPrice"
}
}
}
}
2、巢狀操作
輸出各個目的地航班的最大價格,最小价格
GET kibana_sample_data_flights/_search
{
"size": 0,
"aggs": {
"dest_count":{
"terms": {
"field": "DestCountry"
},
"aggs": {
"max_price": {
"max": {
"field": "AvgTicketPrice"
}
},
"min_price": {
"min": {
"field": "AvgTicketPrice"
}
}
}
}
}
}
3、stats
一次性輸出各種統計結果,包括count、min、max、sum、avg
GET kibana_sample_data_flights/_search
{
"size": 0,
"aggs": {
"my_stats":{
"stats": {
"field": "AvgTicketPrice"
}
}
}
}
4、cardinality
去重後數量統計
GET kibana_sample_data_flights/_search
{
"size": 0,
"aggs": {
"my_cardinality":{
"cardinality": {
"field": "DestCountry"
}
}
}
}
5、top_hits
top_hits 操作,最開頭的幾個文件。
獲取去每個國家的航班的最小价格,下面的“"size": 5”代表獲取5個國家的航班,“"size": 2”代表最低的2個價格。
GET kibana_sample_data_flights/_search
{
"size": 0,
"aggs": {
"my_count": {
"terms": {
"field": "DestCountry",
"size": 5
},
"aggs": {
"my_min_price": {
"top_hits": {
"size": 2,
"sort": [
{
"AvgTicketPrice": {
"order": "asc"
}
}
]
}
}
}
}
}
}
6、ranges 自定義範圍分組
比如下面,小於200一個分組,200到500一個分組,大於500個分組,可以指定輸出的key。
GET kibana_sample_data_flights/_search
{
"size": 0,
"aggs": {
"my_price_range":{
"range": {
"field": "AvgTicketPrice",
"ranges": [
{
"to": 200
},
{
"from": 200,
"to": 500
},
{
"key": ">500",
"from": 500
}
]
}
}
}
}
7、百分位聚合
百分位聚合,可以利用百分位聚合的結果評估資料分佈,判斷資料是否扭曲,判斷資料是否雙峰分佈等。壓測的時候經常使用,比如95百分位對應的值表示這個值大於95%的所有值。假設結果是“10%:12ms ,..., 70%:55ms, 99%:100ms”,說明通常情況下(70%),網頁的響應時間在12ms~55ms,99%的網頁在100ms內載入完成。
GET kibana_sample_data_flights/_search
{
"size": 0,
"aggs": {
"my_price_percentiles":{
"percentiles": {
"field": "AvgTicketPrice",
"percents": [
1,
5,
25,
50,
75,
95,
99
]
}
}
}
}
8、地理邊界聚合
GET kibana_sample_data_flights/_search
{
"size": 0,
"aggs": {
"my_geo_bounds": {
"geo_bounds": {
"field": "DestLocation",
"wrap_longitude": true
}
}
}
}
9、優化 Terms 聚合的效能
設定 eager_global_ordinals 為true,會在記憶體中預先載入這些資料。
七、Pipeline Aggregation
對聚合分析的結果再次做聚合分析。
分兩類
- Sibling:結果和現有分析結果同級。有min_bucket、max_bucket、avg_bucket、sum_bucket、stats_bucket、percentiles_bucket
- Parent:結果內嵌到現有的聚合分析結果之中。有derivative(差值,與前一個的差值,用於看趨勢)、cumulative_sum(累計求和)、moving_avg(移動平均,資料在一個固定大小視窗裡的平均值)
說明,bucket_path引數,指定路徑,如果是二級路徑,注意有一個“>”。
1、Sibling的例子
根據不同的目的地獲取平均票據,並對這些平均票價做分析。
注意,my_distance
,my_avg_price
,my_result
這三個是自定義的變數名,buckets_path指定路徑。
GET kibana_sample_data_flights/_search
{
"size": 0,
"aggs": {
"my_distance": {
"terms": {
"field": "DestCountry"
},
"aggs": {
"my_avg_price": {
"avg": {
"field": "AvgTicketPrice"
}
}
}
},
"my_result": {
"stats_bucket": {
"buckets_path": "my_distance>my_avg_price"
}
}
}
}
2、Parent的例子
統計每50km的平均票價,並檢視其波動
GET kibana_sample_data_flights/_search
{
"size": 0,
"aggs": {
"my_distance": {
"histogram": {
"field": "DistanceKilometers",
"interval": 50
},
"aggs": {
"my_avg_price": {
"avg": {
"field": "AvgTicketPrice"
}
},
"my_result": {
"derivative": {
"buckets_path": "my_avg_price"
}
}
}
}
}
}
八、排序
根據數量(_count)進行排序,數量相同根據返回的key進行排序
GET kibana_sample_data_flights/_search
{
"size": 0,
"aggs": {
"dest_count": {
"terms": {
"field": "DestCountry",
"order": [
{
"_count": "asc"
},
{
"_key": "desc"
}
]
}
}
}
}
根據最終返回的結果進行排序,比如下面的my_stats
GET kibana_sample_data_flights/_search
{
"size": 0,
"aggs": {
"my_distance": {
"terms": {
"field": "DestCountry",
"order": {
"my_stats.min": "asc"
}
},
"aggs": {
"my_stats": {
"stats": {
"field": "AvgTicketPrice"
}
}
}
}
}
}
九、聚合分析的原理及精準度問題
Terms 聚合分析不準的原因,資料分散在多個分片上,Coordinating Node 無法獲取資料全貌。
開啟 show_term_doc_count_error,可以多看到兩個返回值。
- doc_count_error_upper_bound:被遺漏的term 分桶,包含的文件,有可能的最大值
- sum_other_doc_count:除了返回結果 bucket的 terms 以外,其他 terms 的文件總數(總數-返回的總數)
那麼如何解決呢?
- 解決方案 1:當資料量不大時,設定 Primary Shard 為 1;實現準確性。
- 解決方案 2:在分散式資料上,設定 shard_size 引數,提高精確度。原理:每次從 Shard 上額外多獲取資料,提升準確率,但會降低響應時間。
shard_size 的預設大小 “shard_size = size * 1.5 * 10”,可以根據自己的需要進行設定。
十、資料
- https://www.elastic.co/guide/cn/elasticsearch/guide/current/aggregations.html
- https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-aggregations.html
- https://www.elastic.co/guide/en/elasticsearch/reference/7.x/tune-for-search-speed.html
- https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-aggregations-bucket-terms-aggregation.html
- Elasticsearch核心技術與實戰(阮一鳴--極客時間)