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 聚合开始,一步步深入。到时候我会分享一些我在生产环境中踩过的坑,保证让你少走弯路。

嗯,今天就到这里。有问题欢迎留言讨论。