Elasticsearch Aggregations 是什么?bucket 和 metric 聚合怎么理解?
简化版
Aggregations 是 Elasticsearch 的聚合分析能力,可以在搜索结果上做分组和统计。bucket 聚合负责“分桶”,例如按品牌、时间、价格区间分组;metric 聚合负责“算指标”,例如 count、avg、sum、max、min。
详细版
聚合常用于搜索页筛选、报表分析、日志统计和监控看板。
两类最常见聚合:
- bucket aggregation:把文档分组,例如
terms、range、date_histogram。 - metric aggregation:计算指标,例如
avg、sum、min、max、cardinality。
示例:统计不同品牌商品数量和平均价格。
GET /products/_search
{
"size": 0,
"aggs": {
"by_brand": {
"terms": {
"field": "brand.keyword"
},
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
}
}
}
}
}
这里 by_brand 是 bucket 聚合,avg_price 是 metric 聚合。size: 0 表示不关心命中文档列表,只关心统计结果。
面试要补充性能点:高基数字段的 terms 聚合可能很重,聚合字段通常要用 keyword、数值或日期字段,而不是直接用 text。
完整版教学
一、聚合解决什么问题
搜索系统经常不只是“找文档”,还要“看统计”。
例如电商搜索“手机”:
- 每个品牌有多少商品;
- 各价格区间有多少商品;
- 每天新增多少商品;
- 平均价格是多少;
- 最贵和最便宜分别是多少。
这些都可以通过 aggregations 完成。它让 Elasticsearch 不只是搜索引擎,也能承担大量近实时分析查询。
二、bucket 聚合:先把数据分组
bucket 可以理解为“桶”。每个文档根据规则落入一个或多个桶。
按品牌分桶:
"aggs": {
"by_brand": {
"terms": {
"field": "brand.keyword"
}
}
}
按价格区间分桶:
"aggs": {
"price_ranges": {
"range": {
"field": "price",
"ranges": [
{ "to": 100 },
{ "from": 100, "to": 500 },
{ "from": 500 }
]
}
}
}
按时间分桶:
"aggs": {
"per_day": {
"date_histogram": {
"field": "created_at",
"calendar_interval": "day"
}
}
}
三、metric 聚合:再计算指标
metric 聚合负责在桶内计算值。例如:
"aggs": {
"avg_price": { "avg": { "field": "price" } },
"max_price": { "max": { "field": "price" } },
"total_sales": { "sum": { "field": "sales" } }
}
它既可以直接对全体命中文档计算,也可以嵌套在 bucket 里计算每个分组的指标。
例如按品牌分桶后统计平均价格:
"aggs": {
"by_brand": {
"terms": { "field": "brand.keyword" },
"aggs": {
"avg_price": { "avg": { "field": "price" } }
}
}
}
返回结果会变成:
"buckets": [
{ "key": "apple", "doc_count": 120, "avg_price": { "value": 6999 } },
{ "key": "xiaomi", "doc_count": 300, "avg_price": { "value": 2499 } }
]
四、聚合和查询的关系
聚合默认基于 query 命中的文档集合,而不是整个索引。
例如:
GET /orders/_search
{
"size": 0,
"query": {
"term": {
"status": "paid"
}
},
"aggs": {
"sales_per_day": {
"date_histogram": {
"field": "paid_at",
"calendar_interval": "day"
}
}
}
}
这个聚合只统计已支付订单。面试中如果能讲清“query 先限定范围,aggs 再做统计”,就比单纯背 API 更像真正用过。
五、聚合性能和准确性注意点
常见注意事项:
- 聚合字段要选对类型,分组字段通常用
keyword,时间字段用date,指标字段用数值。 - 高基数字段做
terms聚合可能很重,例如用户 id、订单 id。 - 分布式聚合需要各 shard 先局部统计,再由协调节点归并。
cardinality是近似去重,适合大规模估算,不适合强一致精确账务统计。- 聚合结果可能受
size、shard_size、数据分布影响,需要理解误差来源。
如果是强事务、强精确财务统计,通常不要只依赖 Elasticsearch,当作搜索分析侧的近实时统计更合理。
六、常见误区与追问
| 场景 | 更合适的聚合 | 面试要点 |
|---|---|---|
| 按品牌统计商品数 | terms | 字段通常用 brand.keyword,不要直接聚合 text 字段 |
| 按价格段统计 | range | 区间边界要和业务筛选口径一致 |
| 按天看趋势 | date_histogram | 注意 calendar_interval 和时区 |
| 统计独立用户数 | cardinality | 是近似去重,适合大规模分析,不适合财务精确计数 |
记忆钩子:聚合先问“统计范围是谁”,再问“怎么分桶”,最后问“桶里算什么指标”。
举个数字例子:索引里有 1000 万条订单,query 先过滤出最近 7 天已支付的 80 万条,再按 shop_id 分桶并算 sum(amount)。如果不先过滤,聚合会面对全量 1000 万条;如果 shop_id 基数有 50 万,terms 还会产生很重的候选桶合并成本。
- 误区:Aggregations 可以完全替代离线数仓。 Elasticsearch 适合近实时搜索分析和交互式统计,但复杂报表、强一致财务口径、长周期多表计算通常仍应交给数仓或 OLAP 系统。
- 误区:bucket 只是在结果列表上做分组。 聚合默认基于 query 命中的文档集合,不是只基于当前返回的
size条 hits。 - 误区:
cardinality就是精确 distinct count。 它通常使用近似算法,在大规模去重时换取内存和速度,不能直接当作精确对账结果。 - 追问:为什么聚合字段通常用
keyword? 因为聚合需要按完整值分桶,text字段会被分词,按 token 聚合会把原始业务值拆散。 - 追问:
terms聚合为什么可能有误差? 分布式场景下各 shard 先返回局部 top buckets,协调节点再合并,size、shard_size和数据倾斜都会影响最终桶的完整性。 - 追问:聚合慢先排查什么? 先看 query 是否缩小范围、字段类型是否正确、基数是否过高、是否有大规模嵌套聚合,以及 coordinating node 是否承担了过重合并压力。
七、加强记忆
记住“bucket 负责分组,metric 负责算值”。聚合时先用 query 限定文档范围,再用 bucket 分桶,最后用 metric 算指标;高基数字段和 text 字段直接聚合是面试里的重点风险。