1、ES聚合入门:什么是聚合分析、聚合的分类(Metric、Bucket、Pipeline)、聚合与搜索的区别
大家好,我是你们的老朋友。今天咱们正式开始聊 ElasticSearch 的聚合分析。
说实话,我刚开始接触 ES 的时候,觉得它就是个搜索引擎。查查日志、搜搜文档,挺方便的。直到有一次,老板让我统计过去一个月所有订单的金额分布情况——我傻眼了。总不能把几百万条数据全捞出来,自己用代码算吧?
嗯,这时候聚合分析就派上用场了。
1.1 什么是聚合分析?
聚合分析,说白了就是「从数据中提炼出统计信息」。它不是把每一条数据都展示给你看,而是告诉你数据的整体面貌。
举个例子:
- 搜索:找出所有「价格大于100元」的商品
- 聚合:统计「价格在0-50元、50-100元、100元以上」的商品各有多少个
你看,搜索回答的是「有哪些」,聚合回答的是「有多少」。
核心理解:聚合 = 对数据进行分组 + 计算。分组是 Bucket,计算是 Metric。
我个人习惯把聚合想象成 SQL 里的 GROUP BY + 聚合函数。只不过 ES 的聚合更灵活,能嵌套、能链式调用,还能做管道运算。
1.2 聚合的三大分类
ES 的聚合分三类:Metric(指标聚合)、Bucket(桶聚合)、Pipeline(管道聚合)。咱们一个一个说。
1.2.1 Metric 聚合
Metric 聚合就是做数学计算。比如求和、平均值、最大值、最小值、统计数量等等。
我在项目中遇到过这样一个场景:需要实时计算某个接口的平均响应时间。用 avg 聚合,一行代码搞定。
GET /logs/_search
{
"size": 0,
"aggs": {
"avg_response_time": {
"avg": {
"field": "response_time"
}
}
}
}
返回结果也很直观:
{
"aggregations": {
"avg_response_time": {
"value": 235.67
}
}
}
常用的 Metric 聚合有这些:
| 聚合类型 | 说明 | 适用场景 |
|---|---|---|
avg |
平均值 | 平均响应时间、平均价格 |
sum |
求和 | 总销售额、总访问量 |
min |
最小值 | 最低价格、最早时间 |
max |
最大值 | 最高温度、最大并发数 |
value_count |
非空值计数 | 统计有多少条数据有某个字段 |
cardinality |
去重计数 | 统计独立用户数(类似 SQL 的 COUNT DISTINCT) |
stats |
统计汇总 | 一次性返回 count、min、max、avg、sum |
小技巧:如果你需要同时看多个统计值,直接用 stats 聚合,省得写一堆重复代码。
1.2.2 Bucket 聚合
Bucket 聚合就是「分组」。它把数据分到不同的桶里,每个桶里放一组数据。
你想想看,就像把一堆水果按颜色分类——红色的放一筐,绿色的放一筐,黄色的放一筐。每个筐就是一个 Bucket。
最常见的 Bucket 聚合是 terms,按字段值分组:
GET /orders/_search
{
"size": 0,
"aggs": {
"by_category": {
"terms": {
"field": "category",
"size": 10
}
}
}
}
这个查询会按商品类别分组,返回每个类别下的订单数量。
常用的 Bucket 聚合:
| 聚合类型 | 说明 | 适用场景 |
|---|---|---|
terms |
按字段值分组 | 按城市、类别、状态分组 |
range |
按数值范围分组 | 年龄分段、价格区间 |
date_range |
按时间范围分组 | 按周、月、季度统计 |
histogram |
按固定间隔分组 | 每10元一个区间、每小时一个桶 |
date_histogram |
按时间间隔分组 | 按天、按小时统计趋势 |
filter |
按过滤条件分组 | 只统计符合条件的文档 |
注意:terms 聚合默认只返回前10个桶。如果你需要更多,记得设置 size 参数。我曾经因为没设 size,漏掉了重要的数据分组,排查了半天才发现问题。
1.2.3 Pipeline 聚合
Pipeline 聚合有点特别。它不直接对原始数据进行计算,而是对 其他聚合的结果 做二次加工。
说白了,就是「聚合的聚合」。
举个例子:你按天统计了每天的销售额(这是 Bucket 聚合),然后你想算一下这7天的移动平均值——这就是 Pipeline 聚合干的事。
GET /sales/_search
{
"size": 0,
"aggs": {
"sales_per_day": {
"date_histogram": {
"field": "date",
"calendar_interval": "day"
},
"aggs": {
"daily_sales": {
"sum": {
"field": "amount"
}
}
}
},
"moving_avg_sales": {
"moving_fn": {
"buckets_path": "sales_per_day>daily_sales",
"window": 7,
"script": "MovingFunctions.unweightedAvg(values)"
}
}
}
}
你看,moving_avg_sales 这个聚合,它引用了 sales_per_day>daily_sales 这个路径上的结果。这就是 Pipeline 聚合的典型用法。
常用的 Pipeline 聚合:
derivative:计算相邻桶之间的差值(比如日环比)moving_fn:移动窗口计算(移动平均、移动求和)cumulative_sum:累计求和bucket_script:对多个桶的结果做自定义计算bucket_selector:过滤掉不符合条件的桶
避坑指南:Pipeline 聚合的 buckets_path 语法很容易写错。我建议你先用 Kibana 的 Dev Tools 调试,确认路径正确后再写到代码里。
1.3 聚合与搜索的区别
很多新手会混淆聚合和搜索。我刚开始也犯过这个错——以为聚合就是搜索的一种特殊形式。
其实它们俩是两回事:
| 维度 | 搜索(Query) | 聚合(Aggregation) |
|---|---|---|
| 目的 | 找出符合条件的文档 | 统计数据的分布和指标 |
| 返回结果 | 文档列表(hits) | 统计结果(aggregations) |
| 数据量 | 通常返回少量文档 | 处理全量数据 |
| 性能消耗 | 相对较低 | 相对较高(需要扫描大量数据) |
| 典型场景 | 搜索商品、查询日志 | 生成报表、数据看板 |
但有一点很重要:聚合和搜索可以一起用。
比如,你可以先搜索出「2024年所有已支付的订单」,然后对这些订单做聚合,统计每个月的销售额。搜索负责过滤,聚合负责统计,各司其职。
GET /orders/_search
{
"query": {
"bool": {
"filter": [
{ "term": { "status": "paid" } },
{ "range": { "date": { "gte": "2024-01-01", "lte": "2024-12-31" } } }
]
}
},
"size": 0,
"aggs": {
"monthly_sales": {
"date_histogram": {
"field": "date",
"calendar_interval": "month"
},
"aggs": {
"total_amount": {
"sum": {
"field": "amount"
}
}
}
}
}
}
这个查询会先过滤出已支付的订单,再按月分组,最后计算每个月的总金额。搜索和聚合配合得天衣无缝。
记住这个原则:搜索决定「看哪些数据」,聚合决定「怎么分析这些数据」。两者结合,才是 ES 的真正威力。
1.4 小结
好了,咱们来捋一捋今天的内容:
- 聚合分析:从数据中提炼统计信息,回答「有多少」的问题
- Metric 聚合:做数学计算,求和、平均、计数等
- Bucket 聚合:分组,把数据分到不同的桶里
- Pipeline 聚合:对聚合结果做二次加工
- 聚合 vs 搜索:搜索找文档,聚合做统计,可以配合使用
下一章,我会带大家动手写第一个聚合查询。咱们从最简单的 Metric 聚合开始,一步步深入。到时候我会分享一些我在生产环境中踩过的坑,保证让你少走弯路。
嗯,今天就到这里。有问题欢迎留言讨论。