作者:高斌龙,腾讯云大数据 Elasticsearch 高级开发工程师
前言
Elasticsearch 作为一款基于 Lucene 打造的分布式搜索引擎,常用于搜索和日志场景,而在数据分析场景,Elasticsearch 也提供了聚合 Aggregations API 支持完成复杂的查询分析,并且可以使用 Kibana 完成数据的可视化。本文就如何使用 Elasticsearch 进行数据分析做一个简单的介绍。
概览
聚合分析主要为了解决以下问题:
网站的平均加载时间是多久?
根据交易记录来看谁是最有价值的客户?
每个种类的产品数量是多少?
Elasticsearch 的聚合分析 API,主要分为三类:
Metric: 指标,比如平均值、求和、最大值等,都是指标
Bucket: 桶,根据某个字段的值进行的分桶聚合
Pipeline: 管道,不基于索引中的原始数据,而是基于其它的聚合结果再次进行统计分析
Bucket 聚合
Bucket 聚合用于根据指定的字段,统计该字段的不同值的数量,每个不同的值就成为一个 Bucket,聚合结果中会返回不同的 Bucket 中文档的数量。Bucket 聚合的种类也是非常多的,常用的有 Terms 聚合,Date histogram 聚合,Composite 聚合。另外,Bucket 聚合可以包含嵌套的子聚合。
1. Terms 聚合
Terms 聚合支持的字段类型有 Keyword,Numberic, ip, boolean 以及 binary,可以支持统计这些字段类型的字段中不同值的数量。
示例:
请求:
GET /_search
{
"aggs": {
"product": {
"terms": { "field": "productName" }
}
}
}
复制代码
返回结果:
{
...
"aggregations": {
"product": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "books",
"doc_count": 6
},
{
"key": "clothes",
"doc_count": 3
},
{
"key": "shoes",
"doc_count": 2
}
]
}
}
}
复制代码
返回结果中的 buckets 列表默认会按照 doc_count 进行排序,并返回前 10 项。更多参数请参考 Terms aggregation。(https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-terms-aggregation.html)
2. Date histogram 聚合
Date histogram 聚合是对 Date 类型的字段进行统计分析,用于统计一段时间内的文档总数,时间段的起始值即为 Bucket 的 key。
Date histogram 聚合对于每个 Bucket 代表的时间段,又支持两种模式:
Calendar 日历时间段:按日历中的自然时间确定 Bucket,可指定为 minute,hour,day,week,month, quarter,year, 例如指定为 1d,则每个自然日都会产生一个 Bucket,时间跨度从当日的 00:00:00 到 23:59:59.
Fixed 固定时间段:每个 Bucket 代表的时间段跨度是定长的,例如指定为 1d, 则从 1970-01-01 为起始值,并且以文档中 Date 类型字段的起始值所在的 Bucket 为第一个 Bucket,,每隔 24 小时会有一个 bucket 产生,即便这些 bucket 中的文档可能在不同的自然日中产生。
Calendar 时间段示例:
GET/sales/_search?size=0
{
"aggs": {
"sales_over_time": {
"date_histogram": {
"field": "date_field",
"calendar_interval": "month"
}
}
}
}
复制代码
返回结果:
{
...
"aggregations": {
"sales_over_time": {
"buckets": [
{
"key_as_string": "2015-01-01",
"key": 1420070400000,
"doc_count": 3
},
{
"key_as_string": "2015-02-01",
"key": 1422748800000,
"doc_count": 2
},
{
"key_as_string": "2015-03-01",
"key": 1425168000000,
"doc_count": 2
}
]
}
}
}
复制代码
Fixed 固定时间段示例:
GET/sales/_search?size=0
{
"aggs": {
"sales_over_time": {
"date_histogram": {
"field": "date_field",
"fixed_interval": "30d"
}
}
}
}
复制代码
返回结果:
"aggregations": { -
"sales_over_time": { -
"buckets": [ -
{ -
"key_as_string": "2021-12-29T00:00:00.000Z",
"key": 1640736000000,
"doc_count": 1
},
{ -
"key_as_string": "2022-01-28T00:00:00.000Z",
"key": 1643328000000,
"doc_count": 2
},
{ -
"key_as_string": "2022-02-27T00:00:00.000Z",
"key": 1645920000000,
"doc_count": 6
}
]
}
}
复制代码
3. Composite 聚合
Composite 复合聚合是一个 Multi-bucket 聚合,bucket 的 key 可以由多个字段组合而成。Composite 聚合支持分页查询,在 bucket 数量比较多的情况下可以分批次获取聚合结果。
Composite 聚合通过在 sources 参数中指定多个 value source 成分源数据的来构建 multi-bucket 聚合,sources 参数支持的成分源数据类型有 Terms,Histogram, Date Histogram 和 GeoTile Grid 聚合,
如果只在 sources 参数中指定了单一的成分源数据,比如指定为 Terms,那么 Composite 聚合就和普通的 Terms 聚合几乎没有区别了,优点是可以支持分页获取聚合结果。
GET /_search
{
"size": 0,
"aggs": {
"my_buckets": {
"composite": {
"sources": [
{ "product": { "terms": { "field": "product" } } }
]
}
}
}
}
复制代码
Composite 聚合的分页是通过指定 size 参数和 after 参数实现的, size 参数默认为 10, 第一次的查询中会包含一个 after_key 字段表明当前已经的结果中最后一个 bucket 的 key 的值,之后的查询中可以通过指定 after 参数来实现分页获取。
指定 size 参数获取指定数量的聚合结果:
GET /_search
{
"size": 0,
"aggs": {
"my_buckets": {
"composite": {
"size": 2,
"sources": [
{ "date": { "date_histogram": { "field": "timestamp", "calendar_interval": "1d" } } },
{ "product": { "terms": { "field": "product" } } }
]
}
}
}
}
复制代码
返回结果中会包含有 after_key 字段,值为当前结果中最后一个 bucket 的 key:
{
...
"aggregations": {
"my_buckets": {
"after_key": {
"date": 1494288000000,
"product": "mad max"
},
"buckets": [
{
"key": {
"date": 1494201600000,
"product": "rocky"
},
"doc_count": 1
},
{
"key": {
"date": 1494288000000,
"product": "mad max"
},
"doc_count": 2
}
]
}
}
}
复制代码
再次发起请求时,指定 after 字段的值为上一次请求的 after_key,实现分页获取聚合结果:
GET /_search
{
"size": 0,
"aggs": {
"my_buckets": {
"composite": {
"size": 2,
"sources": [
{ "date": { "date_histogram": { "field": "timestamp", "calendar_interval": "1d", "order": "desc" } } },
{ "product": { "terms": { "field": "product", "order": "asc" } } }
],
"after": { "date": 1494288000000, "product": "mad max" }
}
}
}
}
复制代码
Metric 指标聚合
Metric 指标类的聚合诸如 avg 平均值,max 最大值,min 最小值等数值类的聚合,在使用中通常作为一个子聚合。
Max 最大值聚合
Max 最大值聚合用于返回数值类型的字段中的最大值:
GET /_search
{
"size":0,
"aggs": {
"max_price": { "max": { "field": "price" } }
}
}
复制代码
Stats 统计聚合
Stats 统计聚合用于统计字段中值的最小值、最大值、总和、平均值以及文档的总数:
请求示例:
GET /_search
{
"size":0,
"aggs": {
"grades_stats": { "stats": { "field": "grade" } }
}
}
复制代码
返回结果:
{
...
"aggregations": {
"grades_stats": {
"count": 2,
"min": 50.0,
"max": 100.0,
"avg": 75.0,
"sum": 150.0
}
}
}
复制代码
Cardinality 基数聚合
Cardinalit 基数聚合用于统计字段中不同值的数量:
GET /_search
{
"aggs": {
"type_count": {
"cardinality": {
"field": "type"
}
}
}
}
复制代码
Pipeline 管道聚合
Pipeline 聚合基于其它的 Bucket 聚合或 Metric 聚合的结果,再次聚合出新的数据,给原始的聚合结果中增加新的分析数据。Pipeline 聚合主要分为两类:
Pipeline 聚合通过 buckets_path 参数引用父聚合或者兄弟聚合,例如 buckets_path 指定为"my_bucket>my_stats.avg",意即引用名为"my_bucket"的兄弟聚合中的名为"my_stats"子聚合中的 avg 指标项。
Max bucket 聚合
计算销售额最大的月份是哪个月:
GET sales/_search
{
"size": 0,
"aggs": {
"sales_per_month": {
"date_histogram": {
"field": "date",
"calendar_interval": "month" // 按月分桶,统计每个月的销售条目
},
"aggs": {
"sales": {
"sum": {
"field": "price" // 统计每个月的销售总额
}
}
}
},
"max_monthly_sales": {
"max_bucket": {
"buckets_path": "sales_per_month>sales" // 通过引用同一级的sales_per_month聚合中的子聚合sales,计算销售总额最大的月份
}
}
}
}
复制代码
返回结果:
{
"took": 11,
"timed_out": false,
"_shards": ...,
"hits": ...,
"aggregations": {
"sales_per_month": {
"buckets": [
{
"key_as_string": "2015/01/01 00:00:00",
"key": 1420070400000,
"doc_count": 3,
"sales": {
"value": 550.0
}
},
{
"key_as_string": "2015/02/01 00:00:00",
"key": 1422748800000,
"doc_count": 2,
"sales": {
"value": 60.0
}
},
{
"key_as_string": "2015/03/01 00:00:00",
"key": 1425168000000,
"doc_count": 2,
"sales": {
"value": 375.0
}
}
]
},
"max_monthly_sales": {
"keys": ["2015/01/01 00:00:00"], // 销售额最大的月份是2015年1月,总金额为550元
"value": 550.0
}
}
}
复制代码
Stats bucket 聚合
统计各个月销售额的最大值、最小值、平均值、综合和月份数量:
GET sales/_search
{
"size": 0,
"aggs": {
"sales_per_month": {
"date_histogram": {
"field": "date",
"calendar_interval": "month"
},
"aggs": {
"sales": {
"sum": {
"field": "price"
}
}
}
},
"stats_monthly_sales": {
"stats_bucket": {
"buckets_path": "sales_per_month>sales"
}
}
}
}
复制代码
返回结果:
{
"took": 11,
"timed_out": false,
"_shards": ...,
"hits": ...,
"aggregations": {
"sales_per_month": {
"buckets": [
{
"key_as_string": "2015/01/01 00:00:00",
"key": 1420070400000,
"doc_count": 3,
"sales": {
"value": 550.0
}
},
{
"key_as_string": "2015/02/01 00:00:00",
"key": 1422748800000,
"doc_count": 2,
"sales": {
"value": 60.0
}
},
{
"key_as_string": "2015/03/01 00:00:00",
"key": 1425168000000,
"doc_count": 2,
"sales": {
"value": 375.0
}
}
]
},
"stats_monthly_sales": {
"count": 3,
"min": 60.0,
"max": 550.0,
"avg": 328.3333333333333,
"sum": 985.0
}
}
}
复制代码
数据可视化
利用 Kibana 可是实现数据的可视化,可以通过定义查询语句把我们对数据进行分析的结果进行图标化展示。Kibana 针对不同的场景提供了不同的数据可视化使用方式,常用的有 Discover、Dashboard 以及 Maps。
使用 Discover 可以实现数据的检索,常用于日志数据的查询:
使用 Dashboards 可以实现实时的数据分析结果展示,常用于监控、APM 等场景:
使用 Maps 可以实现地理位置信息的展示:
评论