← 返回题目列表

Elasticsearch Aggregations 是什么?bucket 和 metric 聚合怎么理解?

高频 中等 第 17 / 30 题 更新于 2026/07/28
ElasticsearchAggregations聚合数据分析

简化版

Aggregations 是 Elasticsearch 的聚合分析能力,可以在搜索结果上做分组和统计。bucket 聚合负责“分桶”,例如按品牌、时间、价格区间分组;metric 聚合负责“算指标”,例如 count、avg、sum、max、min。

详细版

聚合常用于搜索页筛选、报表分析、日志统计和监控看板。

两类最常见聚合:

  • bucket aggregation:把文档分组,例如 termsrangedate_histogram
  • metric aggregation:计算指标,例如 avgsumminmaxcardinality

示例:统计不同品牌商品数量和平均价格。

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 是近似去重,适合大规模估算,不适合强一致精确账务统计。
  • 聚合结果可能受 sizeshard_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,协调节点再合并,sizeshard_size 和数据倾斜都会影响最终桶的完整性。
  • 追问:聚合慢先排查什么? 先看 query 是否缩小范围、字段类型是否正确、基数是否过高、是否有大规模嵌套聚合,以及 coordinating node 是否承担了过重合并压力。

七、加强记忆

记住“bucket 负责分组,metric 负责算值”。聚合时先用 query 限定文档范围,再用 bucket 分桶,最后用 metric 算指标;高基数字段和 text 字段直接聚合是面试里的重点风险。