Elasticsearch 聚合(Aggregations)详解
目标很明确:
不是教你 API,而是教你在面试中把"聚合"讲清楚、讲专业、讲有取舍。面试官一听就知道:你不是背 DSL 的,是线上真用过的。
1. 一句话先把聚合讲明白(开场必杀)
Elasticsearch 的聚合本质是:在倒排索引之上做分桶(bucket)+ 指标计算(metric),
先按条件筛选文档,再在内存中做统计分析。
如果你再补一句:
它不是 SQL 的 group by,而是分布式、多 shard 并行的统计框架。
------ 面试官基本会点头。
2. 聚合整体执行流程(为什么它"贵")
你要能讲这个流程,而不是只会写 DSL。
- Query 阶段:
- bool / filter / must 先筛选命中文档
- 每个 shard 本地执行聚合:
- 分桶(terms / range / date_histogram)
- 计算指标(count / avg / sum)
- 协调节点 merge:
- 合并各 shard 的 bucket
- 再算 pipeline 聚合(如果有)
👉 结论:
- 聚合 = CPU + 内存密集
- shard 越多、bucket 越多,代价越高
3. 聚合的三大类(必须分类讲)
3.1 Bucket Aggregation(分桶)
回答关键词:"分组"
常见:
terms(最常用)rangedate_histogramcomposite(高级)
例子:
json
"aggs": {
"brandAgg": {
"terms": {
"field": "brand",
"size": 10
}
}
}
3.2 Metric Aggregation(指标)
回答关键词:"数值计算"
常见:
countavgsummin / maxcardinality(去重数)
json
"aggs": {
"avgPrice": {
"avg": { "field": "price" }
}
}
3.3 Pipeline Aggregation(对聚合结果再算)
这是高级工程师加分点
常见:
bucket_sortderivativemoving_avgbucket_script
json
"aggs": {
"brandAgg": {
"terms": { "field": "brand" },
"aggs": {
"avgPrice": { "avg": { "field": "price" } },
"sortBucket": {
"bucket_sort": {
"sort": [{ "avgPrice": { "order": "desc" } }]
}
}
}
}
}
👉 你可以说:
"pipeline 是在协调节点对 bucket 再做一次计算。"
4. terms 聚合:为什么它最容易出事(必问)
4.1 terms 的真实行为
- 每个 shard 先取 top N
- 协调节点再合并
- 不是全局精确 top N
相关参数:
sizeshard_size
json
"terms": {
"field": "brand",
"size": 10,
"shard_size": 100
}
👉 面试官想听你说这句:
terms 默认是近似结果,不是绝对精确。
4.2 高基数字段的坑
- userId、orderId 这类字段
- terms 聚合会:
- 内存暴涨
- GC 压力大
- 甚至 OOM
工程经验回答:
- 控制 size
- 用 composite
- 或离线算(ES 不适合)
5. composite aggregation(真正"像懂的人")
5.1 composite 是干嘛的?
对 bucket 本身做分页
适合场景:
- 品牌很多
- 类目很多
- 需要"翻页展示聚合结果"
5.2 DSL 示例
json
"aggs": {
"brandAgg": {
"composite": {
"size": 100,
"sources": [
{ "brand": { "terms": { "field": "brand" } } }
],
"after": { "brand": "nike" }
}
}
}
👉 类比 search_after,但这是 bucket 的 search_after
6. 聚合 + filter:性能意识的分水岭
❌ 错误写法:
json
"must": [
{ "term": { "status": "ON" } }
]
✅ 正确写法:
json
"filter": [
{ "term": { "status": "ON" } }
]
你要说得出口:
聚合之前的条件,一定尽量放 filter,
不算 score、还能缓存,聚合才跑得动。
7. 聚合 + 分页:面试官常挖的坑
7.1 hits 分页 ≠ 聚合分页
- search_after 只影响 hits
- 聚合 始终基于 query 的全集
👉 这句话说出来就很专业。
7.2 正确工程设计
- 聚合条件不变 → 聚合单独算 / 缓存
- 翻页只翻 hits,不重复算 aggs
8. Spring Boot 解析聚合(你要会)
java
SearchHits<ProductDoc> hits =
elasticsearchOperations.search(query, ProductDoc.class);
ParsedStringTerms terms = hits.getAggregations().get("brandAgg");
for (Terms.Bucket b : terms.getBuckets()) {
String key = b.getKeyAsString();
long count = b.getDocCount();
}
9. 面试官最爱追问的 5 个问题(附回答思路)
Q1:terms 聚合准吗?
不完全准,多 shard 场景是近似,需要调 shard_size 或换 composite。
Q2:为什么不能对 text 聚合?
text 会分词,且不开启 doc_values;应该用 keyword。
Q3:聚合慢怎么优化?
filter 前置、减少 bucket 数、避免高基数字段、必要时离线算。
Q4:聚合能分页吗?
hits 能分页,bucket 用 composite。
Q5:ES 适合做报表吗?
小规模可以,复杂 BI 更适合 ClickHouse / OLAP。
10. 一句话总结(背这个)
聚合不是 group by,
是分布式分桶统计。
bucket 要少,filter 要前,
高基数别硬刚,
真报表别指望 ES。