HOOOS

Elasticsearch聚合揭秘:bucket和metric有何不同 如何协同工作?

0 45 爱钻研的ES小工匠 Elasticsearch聚合bucket vs metric
Apple

Elasticsearch聚合:不只是搜索,更是强大的数据分析引擎

嘿,你好!如果你正在使用Elasticsearch(简称ES),很可能已经体会过它闪电般的搜索速度。但ES的魅力远不止于此。当你的索引里塞满了成千上万甚至数百万的文档(比如用户日志、商品信息、传感器数据),你肯定不满足于仅仅找到它们,更想知道:

  • 哪个牌子的T恤卖得最好?
  • 过去一小时内,我们网站的平均响应时间是多少?
  • 不同价格区间的商品各有多少种?

这些问题,单纯的搜索(query)可回答不了。这时候,就轮到ES的另一个强大武器——**聚合(Aggregations)**登场了!

聚合允许你在数据“现场”进行计算和总结,直接返回分析结果,而不是把成吨的原始数据捞出来再用其他工具处理。这不仅效率高,而且能让你实时洞察数据背后的故事。

但是,刚接触聚合时,很多人会被两个核心概念搞得有点晕:bucket(桶)和 metric(指标)。它们是啥关系?谁负责啥?别急,今天我就带你彻底弄清楚这两个家伙,让你轻松玩转ES聚合分析。

核心概念:bucketmetric,分工明确的两兄弟

想象一下,你面前有一大堆五颜六色的乐高积木(代表你的ES文档)。你想整理一下,并了解一些统计信息。

Bucket(桶)聚合:分类整理大师

bucket聚合的作用,就是按某种规则把积木分门别类地放进不同的桶里。它的核心职责是 分组(Grouping)

  • 规则:可以按照积木的颜色分(红色一桶、蓝色一桶、黄色一桶...)。
  • 规则:也可以按照积木的形状分(方形一桶、长条形一桶、带轮子的一桶...)。
  • 规则:还可以按照积木的大小分(大积木一桶、中积木一桶、小积木一桶...)。

在ES里,“规则”通常就是文档的某个字段值。比如:

  • 按T恤的color字段分桶。
  • 按商品的brand字段分桶。
  • 按日志的timestamp字段按时间段(每小时、每天)分桶。
  • 按产品的price字段按价格区间分桶。

bucket聚合执行完后,你会得到一系列的“桶”,每个桶里都装着符合该桶分类标准的文档。同时,ES还会贴心地告诉你每个桶里有多少个文档(doc_count)。

常见的 bucket 聚合类型有:

  1. terms聚合:最常用!根据字段的确切值进行分组。比如,按T恤颜色(red, blue, green)分桶,每个颜色一个桶。
  2. range聚合:根据数值或日期范围进行分组。比如,按价格分桶:0-50元一个桶,51-100元一个桶,101元以上一个桶。
  3. date_range聚合:专门用于日期范围分组,可以自定义任意范围,比如“上周”、“本月”。
  4. histogram聚合:根据数值字段,按固定间隔分组。比如,按商品重量,每隔100g分一个桶(0-100g, 100-200g, ...)。
  5. date_histogram聚合:根据日期字段,按固定时间间隔分组。这是分析时序数据的利器!比如,按天、按小时、按分钟统计销售额或日志数量。
  6. filters聚合:可以自定义多个查询条件,每个条件对应一个桶。比如,一个桶放所有“红色”且“纯棉”的T恤,另一个桶放所有“蓝色”的T恤。
  7. nested聚合:用于聚合nested类型的字段内部的数据。
  8. significant_terms聚合:找出某个子集(比如某个分类下的商品)相对于全体数据来说,哪些词项(term)是“异常”或“显著”的。常用于发现特征或异常模式。

记住,bucket聚合的核心产出是 “桶的列表”,以及每个桶对应的文档数量。

Metric(指标)聚合:数据计算专家

好了,现在积木已经按颜色分好桶了(bucket聚合干的活)。接下来,你想知道:

  • 每个颜色的积木,平均有多重?
  • 所有红色积木的总价值是多少?
  • 所有积木中,最贵的那块价值多少?
  • 蓝色积木有多少种不同的形状

这些具体的计算任务,就是 metric 聚合的职责。它的核心是 计算(Calculating)

metric聚合通常作用于一个或多个桶内的文档(或者作用于查询匹配的所有文档,如果不结合bucket使用的话),对指定的数值字段进行计算,然后输出一个单一的指标值

常见的 metric 聚合类型有:

  1. avg聚合:计算数值字段的平均值。比如,计算某个分类下商品的平均价格。
  2. sum聚合:计算数值字段的总和。比如,计算某个时间段内的总销售额。
  3. min聚合:找出数值字段的最小值。比如,找到最便宜的商品价格。
  4. max聚合:找出数值字段的最大值。比如,找到最高的访问延迟。
  5. stats聚合:一次性计算出某个数值字段的多个统计指标:count(文档数), min, max, avg, sum。很实用!
  6. extended_stats聚合:比stats更丰富,额外包含平方和、方差、标准差等统计指标。
  7. value_count聚合:计算某个字段有多少个值(即多少文档包含该字段,注意不是去重计数)。
  8. cardinality聚合:计算某个字段的唯一值(基数)的近似数量。比如,统计有多少个独立访客(UV)。注意,为了性能,它通常是近似计算(基于HyperLogLog++算法),但对于大数据量来说非常高效。
  9. percentiles聚合:计算数值字段的百分位数。比如,查看95%的请求响应时间是多少(TP95)。
  10. percentile_ranks聚合:给定一个或多个数值,计算它们处于字段值的哪个百分位排名
  11. top_hits聚合:比较特殊,它不是计算数值,而是从每个桶中抽样出几个“顶级”匹配的文档。比如,找出每个分类下销量最高的3个商品。
  12. scripted_metric聚合:允许你用脚本来自定义复杂的计算逻辑,非常灵活但也需要注意性能。

记住,metric聚合的核心产出是 “计算出的指标值”

强强联合:Bucket + Metric = 精准分析

单独使用bucketmetric有时也有用,但ES聚合真正的威力在于将它们组合起来

通常的模式是:

  1. 先用 bucket 聚合将文档分组
  2. 然后,在每个桶内部(或者说,作用于每个桶里的文档),使用 metric 聚合进行计算

这就像我们刚才的乐高例子:

  1. bucket:按颜色分桶(红桶、蓝桶、黄桶...)。
  2. metric:在每个颜色桶内部,计算积木的平均重量 (avg)。

最终结果就是:“红色积木平均重xx克”,“蓝色积木平均重yy克”,“黄色积木平均重zz克”... 是不是非常清晰?

实战演练:计算每种颜色T恤的平均价格

假设我们有一个名为 tshirts 的索引,里面的文档结构类似这样:

{
  "color": "red",
  "brand": "NiceBrand",
  "price": 19.99,
  "stock": 100
}
{
  "color": "blue",
  "brand": "CoolWear",
  "price": 25.50,
  "stock": 50
}
{
  "color": "red",
  "brand": "AnotherBrand",
  "price": 15.00,
  "stock": 20
}
// ... 更多T恤文档

我们的目标是:计算出每种颜色(color)的T恤的平均价格(price

这显然需要 bucketmetric 联手:

  1. bucket 聚合:按 color 字段分桶(使用 terms 聚合)。
  2. metric 聚合:在每个颜色桶内部,计算 price 字段的平均值(使用 avg 聚合)。

对应的ES查询DSL(领域特定语言)大概是这样的:

GET /tshirts/_search
{
  "size": 0, // 我们不关心具体的T恤文档,只需要聚合结果
  "aggs": { // "aggs" 是 "aggregations" 的缩写
    "group_by_color": { // 这是我们给 bucket 聚合起的名字,可以自定义
      "terms": { "field": "color" }, // 指定使用 terms 聚合,作用于 color 字段
      "aggs": { // 在 bucket 聚合内部,可以嵌套下一层聚合(这里是 metric 聚合)
        "average_price": { // 这是我们给 metric 聚合起的名字
          "avg": { "field": "price" } // 指定使用 avg 聚合,作用于 price 字段
        }
      }
    }
  }
}

解析一下这个查询:

  • "size": 0:告诉ES,我们不想要搜索结果(hits),只对聚合结果感兴趣。这样可以节省网络带宽和处理时间。
  • "aggs":聚合操作的根节点。
  • "group_by_color":这是我们定义的第一个聚合,一个 bucket 聚合。名字可以随便取,只要清晰易懂。
  • "terms": { "field": "color" }:指定了这个 bucket 聚合的类型是 terms,它会根据 color 字段的值来创建桶。
  • "aggs": { ... } (内层):关键点来了! 这个内层的 aggs 定义了要在每个 group_by_color 产生的桶内部执行的子聚合。
  • "average_price":这是我们定义的子聚合(metric 聚合)的名字。
  • "avg": { "field": "price" }:指定了这个子聚合的类型是 avg,它会计算当前桶内所有文档的 price 字段的平均值。

预期的结果(简化版):

ES会返回类似这样的聚合结果:

{
  // ... (省略了took, timed_out等元数据)
  "hits": { // 因为 size=0,所以 hits 数组为空
    "total": { ... },
    "max_score": null,
    "hits": []
  },
  "aggregations": {
    "group_by_color": { // 对应我们定义的 bucket 聚合名字
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 0,
      "buckets": [ // 这里是 bucket 聚合的核心产出:桶的列表
        {
          "key": "red", // 桶的标识,即颜色值
          "doc_count": 150, // 这个桶里有 150 件红色T恤
          "average_price": { // 对应我们定义的 metric 聚合名字
            "value": 17.495 // 计算出的红色T恤的平均价格
          }
        },
        {
          "key": "blue",
          "doc_count": 120,
          "average_price": {
            "value": 22.80
          }
        },
        {
          "key": "green",
          "doc_count": 80,
          "average_price": {
            "value": 19.99
          }
        }
        // ... 其他颜色的桶
      ]
    }
  }
}

解读结果:

看到 aggregations -> group_by_color -> buckets 了吗?这清晰地展示了 bucket 聚合的结果:一个包含多个桶的数组。

  • 每个桶对象都有一个 key(代表这个桶是根据哪个 color 值分出来的)和一个 doc_count(这个桶里有多少文档)。
  • 最妙的是,每个桶对象内部,还包含了我们定义的 metric 聚合 average_price 的结果!比如,keyred 的桶里,average_pricevalue17.495。这就直接告诉我们:红色T恤共有150件,它们的平均价格是17.495元。

是不是一下子就把复杂的数据分析任务变得简单直观了?这就是 bucketmetric 聚合协同工作的魅力!

不仅仅是简单组合:嵌套聚合的威力

你甚至可以进行更复杂的嵌套:

  • Bucket 嵌套 Bucket:先按品牌分桶,然后在每个品牌桶内,再按颜色分桶。这样就能知道“NiceBrand”牌下,红色T恤有多少件,蓝色T恤有多少件。
  • Bucket 嵌套 Bucket 再嵌套 Metric:在上面按品牌再按颜色分桶后,计算每个“品牌-颜色”组合下的平均价格。
GET /tshirts/_search
{
  "size": 0,
  "aggs": {
    "group_by_brand": { // 第一层 bucket: 按品牌
      "terms": { "field": "brand" },
      "aggs": {
        "group_by_color": { // 第二层 bucket: 在品牌桶内按颜色
          "terms": { "field": "color" },
          "aggs": {
            "average_price": { // Metric: 计算品牌-颜色组合的平均价
              "avg": { "field": "price" }
            }
          }
        }
      }
    }
  }
}

这种层层嵌套的能力,让你可以对数据进行多维度、细粒度的钻取分析,构建复杂的数据透视表和报表变得轻而易举。

总结:记住核心分工

现在,你应该对Elasticsearch聚合中的 bucketmetric 有了清晰的认识:

  • Bucket 聚合:负责 “分桶”,将文档按规则 分组。产出是 桶的列表
  • Metric 聚合:负责 “计算”,对桶内(或全体)文档的字段值进行统计运算。产出是 计算出的指标值

它们最常见的用法是组合出击:先用 bucket 把数据切分成有意义的小组,再用 metric 对每个小组进行量化分析。通过灵活的嵌套,你可以构建出功能强大的数据分析应用。

当然,聚合的世界远不止这些。还有管道聚合(Pipeline Aggregations)用于在聚合结果之上再进行聚合(比如计算各分类销售额占总销售额的百分比),以及矩阵聚合(Matrix Aggregations)等更高级的类型。但掌握了 bucketmetric 这两大基石,你就已经打开了ES数据分析的大门。

希望这篇解释能帮你扫清障碍!下次遇到需要分析ES数据的场景,大胆地用聚合吧,你会发现它比你想象的更强大、更方便。动手试试看,用你自己的数据跑几个聚合查询,感觉会更真切!

点评评价

captcha
健康