Elasticsearch 中的聚合(Aggregations)技术详解

Elasticsearch 中的聚合(Aggregations)技术详解

本文来自于我关于 Elasticsearch 的系列文章。欢迎阅读、点评与交流~
1、Elasticsearch 深度解析:从核心原理到开发者实战
2、深入理解倒排索引(Inverted Index):搜索引擎的核心数据结构
3、Elasticsearch 中的聚合(Aggregations)技术详解
4、Elasticsearch 列式存储详解:Doc Values 的原理与实践

文章目录

  • [Elasticsearch 中的聚合(Aggregations)技术详解](#Elasticsearch 中的聚合(Aggregations)技术详解)
    • [1. 聚合概述](#1. 聚合概述)
    • [2. 聚合的分类](#2. 聚合的分类)
    • [3. 核心概念](#3. 核心概念)
    • [4. 常用聚合详解](#4. 常用聚合详解)
      • [4.1 指标聚合(Metric Aggregations)](#4.1 指标聚合(Metric Aggregations))
      • [4.2 桶聚合(Bucket Aggregations)](#4.2 桶聚合(Bucket Aggregations))
      • [4.3 管道聚合(Pipeline Aggregations)](#4.3 管道聚合(Pipeline Aggregations))
    • [5. 聚合语法结构](#5. 聚合语法结构)
    • [6. 执行原理与数据结构](#6. 执行原理与数据结构)
      • [6.1 Doc Values 与 Fielddata](#6.1 Doc Values 与 Fielddata)
      • [6.2 分布式执行流程](#6.2 分布式执行流程)
      • [6.3 深度聚合与延迟](#6.3 深度聚合与延迟)
    • [7. 性能优化与注意事项](#7. 性能优化与注意事项)
      • [7.1 通用优化建议](#7.1 通用优化建议)
      • [7.2 内存与 OOM 风险](#7.2 内存与 OOM 风险)
      • [7.3 实时性与缓存](#7.3 实时性与缓存)
      • [7.4 精确度与近似值](#7.4 精确度与近似值)
    • [8. 典型应用场景](#8. 典型应用场景)
    • [9. 示例:复杂聚合实战](#9. 示例:复杂聚合实战)
    • [10. 总结](#10. 总结)

Elasticsearch 的聚合(Aggregations)是其核心功能之一,用于对索引中的数据进行 统计分析、分组、计算和转换 ,类似于 SQL 中的 GROUP BYCOUNTSUMAVG 以及窗口函数。聚合与搜索查询并行工作,可以在同一个请求中同时返回搜索结果和统计结果,非常适合构建仪表盘、数据报表、实时分析等场景。

1. 聚合概述

  • 定义:聚合是一组对文档集合进行数据提取和计算的框架,它基于查询(query)过滤后的文档集,执行各种统计分析。
  • 与搜索的关系 :聚合可以独立使用(size:0 只返回聚合结果),也可以与搜索查询结合(返回命中文档的同时返回聚合)。
  • 特点
    • 实时计算(基于倒排索引和 Doc Values)
    • 支持嵌套(在桶内再建桶或计算指标)
    • 支持管道聚合(对聚合结果再次聚合)
    • 分布式并行计算(分片级别聚合 + 协调节点合并)

2. 聚合的分类

Elasticsearch 官方将聚合分为四大类,最常用的是前三类:

类型 作用 常见例子
指标聚合 对文档的某个字段进行数学计算,返回单值或多值指标。 avg, sum, min, max, stats, cardinality
桶聚合 将文档划分到不同的桶(分组)中,每个桶代表一个类别或区间。 terms, range, histogram, date_histogram, filter
管道聚合 基于其他聚合的输出结果进行二次聚合,如计算导数、累积和、移动平均等。 derivative, cumulative_sum, moving_avg, bucket_script
矩阵聚合 对多个字段的值进行矩阵运算,如计算相关系数。较少用。 matrix_stats

3. 核心概念

  • 桶(Bucket) :一组满足特定条件的文档集合。桶聚合会创建多个桶,每个桶有一个键(key)和文档列表(逻辑上)。例如按 country 字段分桶,每个国家是一个桶。
  • 指标(Metric):对桶内文档的数值字段进行统计计算。指标聚合可以计算平均值、总和、最大值等。
  • 嵌套(Nesting):桶内可以嵌套子聚合(子桶或子指标),实现多层级分组统计。例如先按国家分桶,再在每个国家桶内按城市分桶,最后计算平均年龄。
  • 元数据(Metadata):可以给聚合添加自定义元数据,便于识别。

4. 常用聚合详解

4.1 指标聚合(Metric Aggregations)

聚合名称 说明 示例
avg 平均值 "avg_price": { "avg": { "field": "price" } }
sum 总和 "total_revenue": { "sum": { "field": "revenue" } }
min / max 最小值 / 最大值 "min_date": { "min": { "field": "timestamp" } }
stats 一次性返回 count, min, max, avg, sum "price_stats": { "stats": { "field": "price" } }
extended_stats stats 基础上增加方差、标准差、标准差范围等 "price_ext_stats": { "extended_stats": { "field": "price" } }
cardinality 去重计数(近似值,基于 HyperLogLog++) "unique_users": { "cardinality": { "field": "user_id" } }
value_count 统计某字段非空的文档数 "non_null_prices": { "value_count": { "field": "price" } }
top_hits 返回桶内最匹配的若干文档(支持排序、分页、高亮) 常嵌套在桶聚合中,获取每个分组的前 N 条记录
percentiles 百分位数统计 "price_percentiles": { "percentiles": { "field": "price" } }
scripted_metric 使用脚本自定义指标计算(灵活但消耗大) 支持 Map/Combine/Reduce 阶段

4.2 桶聚合(Bucket Aggregations)

聚合名称 说明 示例
terms 根据字段值分桶,返回每个值的文档数。常用于枚举、标签、类别。 "by_category": { "terms": { "field": "category", "size": 10 } }
filter 只创建一个桶,包含匹配给定查询的文档。常用于全局过滤。 "high_price": { "filter": { "range": { "price": { "gt": 100 } } } }
filters 多个过滤器,每个过滤器创建一个桶。 "price_ranges": { "filters": { "filters": { "cheap": {...}, "expensive": {...} } } }
range 按数值范围分桶,可自定义多个区间。 "price_range": { "range": { "field": "price", "ranges": [ { "to": 50 }, { "from": 50, "to": 100 } ] } }
date_range 日期范围分桶,支持日期数学表达式(now-1d 等)。 "date_range": { "date_range": { "field": "timestamp", "ranges": [ { "from": "2024-01-01" } ] } }
histogram 等间隔数值直方图(固定步长)。 "price_hist": { "histogram": { "field": "price", "interval": 10 } }
date_histogram 时间直方图(固定时间间隔:分钟、小时、天等)。 "sales_over_time": { "date_histogram": { "field": "timestamp", "calendar_interval": "day" } }
missing 创建"缺失字段值"的桶,存放没有该字段或值为 null 的文档。 "missing_category": { "missing": { "field": "category" } }
geohash_grid 地理点按 Geohash 网格分桶,用于地图聚合。 "geo_grid": { "geohash_grid": { "field": "location", "precision": 5 } }
composite 用于分页遍历多层级聚合结果,类似 SQL 的 GROUP BY 分页。 避免 terms 聚合深度分页的性能问题

4.3 管道聚合(Pipeline Aggregations)

管道聚合不是从文档直接计算,而是从其他聚合的输出(多桶或单值)中计算。必须明确指定 buckets_path 指向父聚合的结果。

聚合名称 说明 示例场景
derivative 计算某指标在相邻桶之间的导数(变化率)。 每小时订单数的变化趋势
cumulative_sum 累积和,从第一个桶到当前桶的累加值。 年度销售额累计
moving_avg / mov_fn 移动平均(平滑时间序列)。7.x 后推荐 mov_fn 支持自定义函数。 股票价格 7 日移动平均
avg_bucket 计算所有桶内某指标的平均值。 平均每个类别的销售额
sum_bucket 所有桶内某指标的总和。 所有类别销售额的总和
min_bucket / max_bucket 所有桶内某指标的最小值/最大值及其对应的桶键。 找出销量最高的类别
stats_bucket 类似 stats,但作用于多桶指标。 对每个月的销售额进行统计
bucket_script 使用脚本基于多个聚合结果计算新值(每个桶执行一次)。 计算比率 total_sales / total_visitors
bucket_selector 过滤掉不符合脚本条件的桶。 只保留销售额大于 1000 的月份
serial_diff 计算某指标与前一滞后期的差值。 周期差分(去季节趋势)

5. 聚合语法结构

聚合通过 aggsaggregations 关键字定义,可以嵌套任意深度。

基本结构:

json 复制代码
{
  "size": 0,   // 不返回文档,只返回聚合结果
  "query": { ... },   // 可选,限定聚合的文档范围
  "aggs": {
    "my_agg_name_1": {   // 自定义聚合名称
      "agg_type": {      // 聚合类型,如 terms, avg
        // 聚合参数
      }
    },
    "my_agg_name_2": {
      "agg_type": {
        // ...
      },
      "aggs": {          // 子聚合(嵌套)
        "sub_agg": { ... }
      }
    }
  }
}

示例:按类别分桶,计算每类商品的平均价格和最高价格,再按品牌子分桶

json 复制代码
{
  "size": 0,
  "aggs": {
    "categories": {
      "terms": { "field": "category", "size": 10 },
      "aggs": {
        "avg_price": { "avg": { "field": "price" } },
        "max_price": { "max": { "field": "price" } },
        "brands": {
          "terms": { "field": "brand", "size": 5 },
          "aggs": {
            "brand_avg_price": { "avg": { "field": "price" } }
          }
        }
      }
    }
  }
}

6. 执行原理与数据结构

6.1 Doc Values 与 Fielddata

  • Doc Values:Elasticsearch 默认对非文本字段启用的列式存储结构,用于排序和聚合。它基于磁盘,在索引时生成,聚合时直接读取,内存效率高。
  • Fielddata :对于 text 字段,默认禁用 Doc Values,聚合时需启用 fielddata(将倒排索引加载到内存),非常消耗内存,不推荐 生产环境对 text 字段做聚合。建议使用 keyword 类型。

6.2 分布式执行流程

  1. 协调节点接收聚合请求,将请求广播到所有分片(或根据路由指定分片)。
  2. 每个分片独立执行聚合:读取本地 Doc Values,计算该分片的桶和指标。
  3. 协调节点 收集所有分片的部分结果,进行合并、排序、分页等二次处理(例如 terms 聚合的 size 取 Top K,需要分片返回 Top 候选,再全局排序)。
  4. 返回最终聚合结果

6.3 深度聚合与延迟

  • 对于 terms 聚合,如果 size 很大(如 10000),每个分片需要返回该数量候选桶,协调节点再合并,性能会下降。
  • 使用 composite 聚合可以分页获取全量分组,避免一次性取大量桶。
  • date_histogram 等时间聚合使用固定间隔,性能较好。

7. 性能优化与注意事项

7.1 通用优化建议

  • 使用 Doc Values :确保聚合字段是 keyword、数值或日期类型,不要对 text 进行聚合。
  • 限制桶数量 :在 terms 聚合中设置合理的 size,使用 shard_size 控制分片返回的候选桶数。
  • 过滤前置 :先通过 query 缩小聚合数据范围,减少计算量。
  • 并行聚合:Elasticsearch 默认并行执行同级聚合,但过多深层嵌套会占用内存。
  • 利用 execution_hintterms 聚合支持 map(直接扫描)或 global_ordinals(使用全局序数),后者对大基数更快。
  • 避免 scripted_metric:除非万不得已,尽量用内置聚合代替脚本聚合。

7.2 内存与 OOM 风险

  • 桶聚合的结果需要加载到内存(分片和协调节点),大量桶(如上百万)可能导致 OutOfMemoryError。
  • 使用 composite 聚合可以流式获取桶,避免一次性加载所有桶。
  • 监控节点内存,配置 search.max_buckets(默认 65535)限制单个响应中的桶总数。

7.3 实时性与缓存

  • 聚合是实时计算的(每次请求都重新读取文档),但分片查询结果有缓存(request cache)。
  • 如果数据不变,可以启用查询缓存提升性能,但注意缓存的粒度是分片级别。

7.4 精确度与近似值

  • cardinality 聚合基于 HyperLogLog++,默认精度可控(precision_threshold),是近似值。
  • percentiles 使用 TDigest 算法,默认也是近似值(精度与压缩参数相关)。
  • 对精度要求极高时,可以增加 precision_threshold 或使用脚本精确计算,但性能下降。

8. 典型应用场景

  1. 电商仪表盘 :按品类统计销售额、订单量、平均价格;按月/周展示销售趋势(date_histogram + sum)。
  2. 日志分析 :按 status_code 分桶统计各 HTTP 状态码数量;按小时统计错误率(管道聚合计算导数)。
  3. 用户画像 :计算用户年龄的百分位数;按地区分组统计活跃用户数(cardinality 去重)。
  4. 监控告警 :使用 avg_bucket 计算过去 10 分钟的平均 CPU 使用率,超过阈值触发告警。
  5. 地理分析geohash_grid 聚合在地图上展示热力分布;geo_distance 范围聚合分析附近兴趣点数量。

9. 示例:复杂聚合实战

需求:统计最近 7 天,每个商品类别的销售总额、平均折扣、唯一买家数,并计算每天销售额的 7 日移动平均。

json 复制代码
{
  "size": 0,
  "query": {
    "range": { "order_date": { "gte": "now-7d" } }
  },
  "aggs": {
    "by_category": {
      "terms": { "field": "category", "size": 10 },
      "aggs": {
        "total_sales": { "sum": { "field": "price" } },
        "avg_discount": { "avg": { "field": "discount_rate" } },
        "unique_buyers": { "cardinality": { "field": "user_id" } },
        "sales_over_time": {
          "date_histogram": {
            "field": "order_date",
            "calendar_interval": "day"
          },
          "aggs": {
            "daily_sales": { "sum": { "field": "price" } },
            "sales_7_day_ma": {
              "moving_avg": { "buckets_path": "daily_sales", "window": 7 }
            }
          }
        }
      }
    }
  }
}

10. 总结

  • Elasticsearch 聚合提供了强大的实时数据分析能力,涵盖了从基础统计到复杂管道计算的需求。
  • 核心分类:指标聚合 (计算值)、桶聚合 (分组)、管道聚合(二次计算)。
  • 性能关键:依赖 Doc Values 列存,避免 text 字段聚合;控制桶数量,必要时使用 composite 分页。
  • 适用场景:任何需要在不离线的情况下对大规模数据进行多维分析、趋势计算、报表生成的系统。

理解聚合的工作机制和优化技巧,可以帮助你高效地构建搜索与分析一体化的应用,充分发挥 Elasticsearch 的实时性优势。

相关推荐
Promise微笑4 分钟前
2026年国产替代油介损测试仪:油介损全场景解决方案与技术演进
大数据·网络·人工智能
workflower19 分钟前
具身智能行业应用-生活服务业
大数据·人工智能·机器人·动态规划·生活
志栋智能1 小时前
超自动化安全:构建智能安全运营的核心引擎
大数据·运维·服务器·数据库·安全·自动化·产品运营
xiaoduo AI2 小时前
客服机器人非工作时间能休眠?智能Agent开放平台定时唤醒,无人值守省资源?
大数据·人工智能·机器人
好赞科技3 小时前
深度测评2026年精选美发预约小程序排行榜 革新预约新体验 修订
大数据·微信小程序
集和诚JHCTECH4 小时前
BRAV-7120加持,让有毒有害气体无处遁形
大数据·人工智能·嵌入式硬件
互联网志5 小时前
加速高校科技成果转化 赋能实体经济高质量发展
大数据·人工智能·物联网
李可以量化6 小时前
DeepSeek 量化交易实战:用标准化提示词模板实现 AI 辅助交易决策
大数据·数据库·人工智能
学掌门6 小时前
数据分析师职业规划——数据分析师的职业焦虑与未来发展
大数据·信息可视化
亚马逊云开发者6 小时前
EMR Core 节点部署 Flink Client 实战:Bootstrap Action 一次打包多次复用,解决调度系统提交任务的痛点
大数据·flink·bootstrap