【ElasticSearch从入门到架构师】第7章-聚合查询——实现数据统计与分析


导语:如果说全文检索是 ElasticSearch 的"看家本领",那么聚合查询就是它的"数据大脑"。从电商平台的销售报表,到日志系统的异常监控,再到实时大屏的数据看板------聚合查询是让海量数据"开口说话"的关键能力。本章将从核心概念出发,逐步深入到嵌套聚合、排序优化等高级场景,帮你彻底掌握 ElasticSearch 的数据分析能力。


一、聚合查询核心概念:桶聚合与指标聚合

在 ElasticSearch 中,聚合(Aggregation)是对数据进行分组统计的核心机制。它的设计灵感来自 SQL 中的 GROUP BY + 聚合函数,但能力远超 SQL。

1.1 为什么要学聚合?

先看一个真实场景:

你负责一个电商平台的搜索服务。运营同学找到你:

  • "帮我统计各品类的月销售额"
  • "用户下单金额的分布情况如何?"
  • "哪个价格区间的商品转化率最高?"

这些需求,本质上都是聚合查询。没有聚合,ElasticSearch 只是一个搜索引擎;有了聚合,它就是一个强大的实时分析引擎。

1.2 聚合的两大核心类型

ElasticSearch 将聚合分为两大类,理解它们的区别是掌握聚合的第一步:

(1)桶聚合(Bucket Aggregation)------"把数据分到不同的桶里"

桶聚合的作用类似于 SQL 的 GROUP BY,它根据某个条件将文档划分到不同的"桶"(Bucket)中,每个桶包含一组满足条件的文档。

复制代码
以"商品品类"为例:

┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐
│  数码产品  │  │  服饰鞋包  │  │  食品饮料  │  │  家居家装  │
│  (桶A)    │  │  (桶B)    │  │  (桶C)    │  │  (桶D)    │
│  1250条   │  │  3420条   │  │  890条    │  │  2100条   │
└──────────┘  └──────────┘  └──────────┘  └──────────┘

(2)指标聚合(Metric Aggregation)------"对桶里的数据做计算"

指标聚合对一组文档的数值字段进行数学运算,计算出一个或多个指标值。它类似于 SQL 中的 SUM()AVG()COUNT(DISTINCT) 等聚合函数。

复制代码
对"数码产品"这个桶做指标聚合:

  数码产品桶
  ┌─────────────────────────────────┐
  │  SUM(销售额) = ¥1,250,000       │
  │  AVG(单价)   = ¥2,350           │
  │  MAX(单价)   = ¥12,999          │
  │  MIN(单价)   = ¥29              │
  └─────────────────────────────────┘

1.3 聚合查询的基本语法

聚合查询嵌套在查询请求的 aggs(或 aggregations)字段中:

json 复制代码
GET /products/_search
{
  "size": 0,
  "aggs": {
    "聚合名称": {
      "聚合类型": {
        "字段配置": "值"
      }
    }
  }
}

关键参数说明:

参数 说明
size: 0 不返回原始文档,只返回聚合结果(推荐做法,减少内存消耗)
aggs 聚合查询的顶层关键字,与 aggregations 等价
聚合名称 自定义名称,用于在结果中标识该聚合
聚合类型 桶聚合或指标聚合的具体类型(如 termssum 等)

1.4 管道聚合(Pipeline Aggregation)

除了桶聚合和指标聚合,还有一种特殊的聚合类型------管道聚合。它的输入是其他聚合的输出结果,而非原始文档。常见用法包括:

  • 衍生桶聚合:对现有桶进一步计算
  • 衍生指标聚合:对指标结果做二次计算
  • 移动平均:计算时间序列的移动平均值

本章重点讲解桶聚合和指标聚合,管道聚合将在后续章节单独展开。


二、常用指标聚合:求和、平均值、最大值、最小值、去重计数

指标聚合是最基础也是最常用的聚合类型。我们以一个电商订单索引为例进行讲解。

2.1 准备测试数据

首先创建一个订单索引并插入测试数据:

json 复制代码
# 创建索引映射
PUT /orders
{
  "mappings": {
    "properties": {
      "order_id":      { "type": "keyword" },
      "user_id":       { "type": "keyword" },
      "category":      { "type": "keyword" },
      "product_name":  { "type": "keyword" },
      "price":         { "type": "double" },
      "quantity":       { "type": "integer" },
      "status":        { "type": "keyword" },
      "order_date":    { "type": "date", "format": "yyyy-MM-dd" }
    }
  }
}

# 批量插入数据(bulk)
POST /orders/_bulk
{"index":{"_id":"1"}}
{"order_id":"ORD001","user_id":"U001","category":"数码","product_name":"iPhone 15","price":7999,"quantity":1,"status":"已完成","order_date":"2024-03-01"}
{"index":{"_id":"2"}}
{"order_id":"ORD002","user_id":"U002","category":"服饰","product_name":"春季外套","price":399,"quantity":2,"status":"已完成","order_date":"2024-03-02"}
{"index":{"_id":"3"}}
{"order_id":"ORD003","user_id":"U001","category":"数码","product_name":"AirPods Pro","price":1899,"quantity":1,"status":"已完成","order_date":"2024-03-03"}
{"index":{"_id":"4"}}
{"order_id":"ORD004","user_id":"U003","category":"食品","product_name":"有机坚果礼盒","price":168,"quantity":3,"status":"已发货","order_date":"2024-03-04"}
{"index":{"_id":"5"}}
{"order_id":"ORD005","user_id":"U002","category":"家居","product_name":"智能台灯","price":259,"quantity":1,"status":"已完成","order_date":"2024-03-05"}
{"index":{"_id":"6"}}
{"order_id":"ORD006","user_id":"U004","category":"数码","product_name":"iPad Air","price":4799,"quantity":1,"status":"已取消","order_date":"2024-03-06"}
{"index":{"_id":"7"}}
{"order_id":"ORD007","user_id":"U003","category":"服饰","product_name":"运动鞋","price":599,"quantity":1,"status":"已完成","order_date":"2024-03-07"}
{"index":{"_id":"8"}}
{"order_id":"ORD008","user_id":"U001","category":"食品","product_name":"进口红酒","price":328,"quantity":2,"status":"已完成","order_date":"2024-03-08"}
{"index":{"_id":"9"}}
{"order_id":"ORD009","user_id":"U005","category":"数码","product_name":"机械键盘","price":699,"quantity":1,"status":"已完成","order_date":"2024-03-09"}
{"index":{"_id":"10"}}
{"order_id":"ORD010","user_id":"U004","category":"家居","product_name":"空气净化器","price":1899,"quantity":1,"status":"已发货","order_date":"2024-03-10"}

2.2 Sum(求和)

计算某个数值字段的总和,最经典的指标聚合。

json 复制代码
GET /orders/_search
{
  "size": 0,
  "aggs": {
    "total_revenue": {
      "sum": {
        "field": "price"
      }
    }
  }
}

返回结果:

json 复制代码
{
  "aggregations": {
    "total_revenue": {
      "value": 19036.0
    }
  }
}

实战技巧 :计算订单总金额时,通常需要 price * quantity,可以配合 脚本聚合 或在索引时预计算一个 total_amount 字段。

2.3 Avg(平均值)

json 复制代码
GET /orders/_search
{
  "size": 0,
  "aggs": {
    "avg_price": {
      "avg": {
        "field": "price"
      }
    }
  }
}

返回结果:

json 复制代码
{
  "aggregations": {
    "avg_price": {
      "value": 1903.6
    }
  }
}

2.4 Max(最大值) & Min(最小值)

json 复制代码
GET /orders/_search
{
  "size": 0,
  "aggs": {
    "max_price": {
      "max": { "field": "price" }
    },
    "min_price": {
      "min": { "field": "price" }
    }
  }
}

返回结果:

json 复制代码
{
  "aggregations": {
    "max_price": { "value": 7999.0 },
    "min_price": { "value": 168.0 }
  }
}

2.5 Cardinality(去重计数)

Cardinality 用于计算某个字段的不同值的数量,等价于 SQL 的 COUNT(DISTINCT field)。底层使用 HyperLogLog++ 算法,在精度和内存之间做了平衡。

json 复制代码
GET /orders/_search
{
  "size": 0,
  "aggs": {
    "unique_users": {
      "cardinality": {
        "field": "user_id"
      }
    }
  }
}

返回结果:

json 复制代码
{
  "aggregations": {
    "unique_users": {
      "value": 5
    }
  }
}

精度控制 :Cardinality 默认精度阈值是 3000。对于大数据量,可以通过 precision_threshold 参数调整精度:

json 复制代码
"cardinality": {
  "field": "user_id",
  "precision_threshold": 10000
}

精度越高,内存消耗越大。通常设置为期望去重值的整数倍即可。

2.6 Stats(统计集合)

Stats 是一个复合指标聚合,一次性返回 countminmaxavgsum 五个统计值:

json 复制代码
GET /orders/_search
{
  "size": 0,
  "aggs": {
    "price_stats": {
      "stats": { "field": "price" }
    }
  }
}

返回结果:

json 复制代码
{
  "aggregations": {
    "price_stats": {
      "count": 10,
      "min": 168.0,
      "max": 7999.0,
      "avg": 1903.6,
      "sum": 19036.0
    }
  }
}

2.7 Extended Stats(扩展统计)

Extended Stats 在 Stats 的基础上增加了方差、标准差、平方和等高级统计指标:

json 复制代码
GET /orders/_search
{
  "size": 0,
  "aggs": {
    "price_extended_stats": {
      "extended_stats": { "field": "price" }
    }
  }
}

返回结果包含:countminmaxavgsumsum_of_squaresvariancevariance_populationvariance_samplingstd_deviationstd_deviation_populationstd_deviation_sampling

应用场景:当需要分析数据的离散程度时,标准差和方差是非常有用的指标。比如判断价格分布是否异常集中或分散。


三、常用桶聚合:Terms、Range、Date Range、Histogram

桶聚合负责将文档分到不同的"桶"中,是最灵活的聚合类型。

3.1 Terms 聚合------按词条分桶

Terms 聚合根据某个字段的值进行分桶,类似于 SQL 的 GROUP BY。它是使用频率最高的桶聚合。

基本用法------统计各品类的订单数量:

json 复制代码
GET /orders/_search
{
  "size": 0,
  "aggs": {
    "by_category": {
      "terms": {
        "field": "category",
        "size": 10
      }
    }
  }
}

返回结果:

json 复制代码
{
  "aggregations": {
    "by_category": {
      "buckets": [
        { "key": "数码", "doc_count": 4 },
        { "key": "服饰", "doc_count": 2 },
        { "key": "食品", "doc_count": 2 },
        { "key": "家居", "doc_count": 2 }
      ]
    }
  }
}

核心参数详解:

参数 说明 默认值
size 返回的桶数量 10
order 桶的排序方式 _count 降序
min_doc_count 最小文档数阈值 1
shard_size 每个分片返回的桶数量 size
show_term_doc_count_error 是否显示近似误差 false

进阶配置:

json 复制代码
GET /orders/_search
{
  "size": 0,
  "aggs": {
    "by_category": {
      "terms": {
        "field": "category",
        "size": 10,
        "order": {
          "_count": "desc"
        },
        "min_doc_count": 1,
        "shard_size": 50
      }
    }
  }
}

重要概念------Terms 聚合的近似性 :Terms 聚合是分布式的,协调节点向每个分片请求 top N 的结果,然后合并。如果某个词条均匀分布在所有分片上,它的全局计数可能被低估。增大 shard_size 可以提高准确性,但会增加内存消耗。

3.2 Range 聚合------按数值范围分桶

Range 聚合根据数值范围将文档分到不同的桶中。每个桶由 from(包含)和 to(不包含)定义。

统计商品价格区间分布:

json 复制代码
GET /orders/_search
{
  "size": 0,
  "aggs": {
    "price_ranges": {
      "range": {
        "field": "price",
        "ranges": [
          { "key": "低价区", "from": 0, "to": 500 },
          { "key": "中价区", "from": 500, "to": 2000 },
          { "key": "高价区", "from": 2000, "to": 5000 },
          { "key": "奢侈品区", "from": 5000 }
        ]
      }
    }
  }
}

返回结果:

json 复制代码
{
  "aggregations": {
    "price_ranges": {
      "buckets": [
        { "key": "低价区", "from": 0.0, "to": 500.0, "doc_count": 3 },
        { "key": "中价区", "from": 500.0, "to": 2000.0, "doc_count": 4 },
        { "key": "高价区", "from": 2000.0, "to": 5000.0, "doc_count": 2 },
        { "key": "奢侈品区", "from": 5000.0, "doc_count": 1 }
      ]
    }
  }
}

注意from 是包含的(≥),to 是不包含的(<)。如果只指定 from 不指定 to,则表示无上限;反之同理。

3.3 Date Range 聚合------按日期范围分桶

Date Range 是 Range 聚合的日期版本,专门处理日期字段。日期格式支持数学表达式,如 now-7d/d

按时间维度统计订单分布:

json 复制代码
GET /orders/_search
{
  "size": 0,
  "aggs": {
    "order_time_ranges": {
      "date_range": {
        "field": "order_date",
        "format": "yyyy-MM-dd",
        "ranges": [
          { "key": "本周", "from": "now-7d/d", "to": "now/d" },
          { "key": "上周", "from": "now-14d/d", "to": "now-7d/d" },
          { "key": "更早", "to": "now-14d/d" }
        ]
      }
    }
  }
}

日期表达式速查:

表达式 含义
now 当前时间
now-1d 1天前
now-7d/d 7天前(向下取整到天)
now/M 当前月的开始
now-1M/M 上个月的开始
now+1h/h 下一小时的开始

实战建议 :日期表达式中 /d/M 等后缀表示"取整到该精度",这对于时间范围聚合非常重要,能避免边界数据遗漏。

3.4 Histogram 聚合------等间距直方图分桶

Histogram 聚合按固定间隔将数值字段分成若干桶。每个桶的中心值、左右边界都是自动计算的。

以 1000 为间隔统计价格分布:

json 复制代码
GET /orders/_search
{
  "size": 0,
  "aggs": {
    "price_histogram": {
      "histogram": {
        "field": "price",
        "interval": 1000,
        "min_doc_count": 0,
        "extended_bounds": {
          "min": 0,
          "max": 8000
        }
      }
    }
  }
}

返回结果:

json 复制代码
{
  "aggregations": {
    "price_histogram": {
      "buckets": [
        { "key": 0.0, "doc_count": 3 },
        { "key": 1000.0, "doc_count": 4 },
        { "key": 2000.0, "doc_count": 2 },
        { "key": 3000.0, "doc_count": 0 },
        { "key": 4000.0, "doc_count": 0 },
        { "key": 5000.0, "doc_count": 0 },
        { "key": 6000.0, "doc_count": 0 },
        { "key": 7000.0, "doc_count": 1 },
        { "key": 8000.0, "doc_count": 0 }
      ]
    }
  }
}

核心参数说明:

参数 说明
interval 桶的间隔大小
min_doc_count 最小文档数,设为 0 可显示空桶
extended_bounds 强制扩展显示范围
hard_bounds 限制桶的上下界
missing 缺失值的处理方式

3.5 Date Histogram 聚合------时间序列分析利器

Date Histogram 是 Histogram 的时间版本,按固定时间间隔分桶,是时序分析最常用的聚合。

json 复制代码
GET /orders/_search
{
  "size": 0,
  "aggs": {
    "orders_over_time": {
      "date_histogram": {
        "field": "order_date",
        "calendar_interval": "day",
        "format": "yyyy-MM-dd",
        "min_doc_count": 0,
        "extended_bounds": {
          "min": "2024-03-01",
          "max": "2024-03-10"
        }
      }
    }
  }
}

时间间隔选项:

间隔类型 可选值
calendar_interval minutehourdayweekmonthquarteryear
fixed_interval 1ms1s1m1h1d(支持倍数如 30s2h

选择建议calendar_interval 适合自然时间(如"每月"会自动处理不同月份的天数差异),fixed_interval 适合固定间隔(如"每30天"严格按30天计算)。


四、多级聚合、嵌套聚合实战

真正的业务分析往往需要将多个聚合组合使用。嵌套聚合是 ElasticSearch 聚合能力的精髓所在。

4.1 桶内嵌套指标聚合

最简单的嵌套形式:在桶聚合内部添加指标聚合。

需求:统计各品类的总销售额和平均客单价:

json 复制代码
GET /orders/_search
{
  "size": 0,
  "aggs": {
    "by_category": {
      "terms": { "field": "category", "size": 10 },
      "aggs": {
        "total_sales": {
          "sum": { "field": "price" }
        },
        "avg_sales": {
          "avg": { "field": "price" }
        },
        "order_count": {
          "value_count": { "field": "order_id" }
        }
      }
    }
  }
}

返回结果:

json 复制代码
{
  "aggregations": {
    "by_category": {
      "buckets": [
        {
          "key": "数码",
          "doc_count": 4,
          "total_sales": { "value": 15396.0 },
          "avg_sales": { "value": 3849.0 },
          "order_count": { "value": 4 }
        },
        {
          "key": "服饰",
          "doc_count": 2,
          "total_sales": { "value": 998.0 },
          "avg_sales": { "value": 499.0 },
          "order_count": { "value": 2 }
        }
        // ... 其他品类
      ]
    }
  }
}

4.2 桶内嵌套桶聚合(多级分桶)

桶聚合可以嵌套另一个桶聚合,实现多维度下钻分析。

需求:先按品类分组,再按价格区间分组:

json 复制代码
GET /orders/_search
{
  "size": 0,
  "aggs": {
    "by_category": {
      "terms": { "field": "category", "size": 10 },
      "aggs": {
        "by_price_range": {
          "range": {
            "field": "price",
            "ranges": [
              { "key": "500以下", "to": 500 },
              { "key": "500-2000", "from": 500, "to": 2000 },
              { "key": "2000以上", "from": 2000 }
            ]
          }
        }
      }
    }
  }
}

返回结果结构:

复制代码
by_category
  ├── 数码 (4)
  │   ├── 500以下: 0
  │   ├── 500-2000: 2
  │   └── 2000以上: 2
  ├── 服饰 (2)
  │   ├── 500以下: 1
  │   ├── 500-2000: 1
  │   └── 2000以上: 0
  └── ...

4.3 三级嵌套:品类 → 时间 → 指标

需求:统计每个品类每天的销售总额和订单数:

json 复制代码
GET /orders/_search
{
  "size": 0,
  "aggs": {
    "by_category": {
      "terms": { "field": "category", "size": 10 },
      "aggs": {
        "by_day": {
          "date_histogram": {
            "field": "order_date",
            "calendar_interval": "day",
            "format": "yyyy-MM-dd"
          },
          "aggs": {
            "daily_revenue": {
              "sum": { "field": "price" }
            },
            "daily_orders": {
              "value_count": { "field": "order_id" }
            }
          }
        }
      }
    }
  }
}

架构师视角:这种三级嵌套(品类 → 日期 → 指标)是数据看板最常用的聚合模式。前端可以据此渲染出品类选择器 + 时间折线图 + 指标卡片的完整交互。

4.4 实战:电商销售分析仪表盘

将所学知识综合运用,构建一个完整的销售分析聚合查询:

json 复制代码
GET /orders/_search
{
  "size": 0,
  "query": {
    "bool": {
      "filter": [
        { "term": { "status": "已完成" } },
        { "range": { "order_date": { "gte": "2024-03-01", "lte": "2024-03-31" } } }
      ]
    }
  },
  "aggs": {
    "总销售额": {
      "sum": { "field": "price" }
    },
    "平均客单价": {
      "avg": { "field": "price" }
    },
    "独立用户数": {
      "cardinality": { "field": "user_id" }
    },
    "各品类销售统计": {
      "terms": { "field": "category", "size": 10, "order": { "品类销售额": "desc" } },
      "aggs": {
        "品类销售额": {
          "sum": { "field": "price" }
        },
        "品类订单数": {
          "value_count": { "field": "order_id" }
        },
        "品类均价": {
          "avg": { "field": "price" }
        },
        "每日趋势": {
          "date_histogram": {
            "field": "order_date",
            "calendar_interval": "day",
            "format": "yyyy-MM-dd",
            "min_doc_count": 0,
            "extended_bounds": {
              "min": "2024-03-01",
              "max": "2024-03-31"
            }
          },
          "aggs": {
            "日销售额": {
              "sum": { "field": "price" }
            }
          }
        }
      }
    },
    "价格分布": {
      "histogram": {
        "field": "price",
        "interval": 1000,
        "min_doc_count": 0,
        "extended_bounds": { "min": 0, "max": 8000 }
      }
    }
  }
}

这段聚合实现了:

  1. 全局维度:总销售额、平均客单价、独立用户数
  2. 品类维度:每个品类的销售额、订单数、均价
  3. 时间维度:每个品类每天的销售额趋势
  4. 价格维度:商品价格的整体分布

注意 :聚合查询可以与 query 条件组合使用。上面的查询先通过 bool filter 筛选出"已完成"且在3月份内的订单,再对筛选后的数据做聚合。这相当于 SQL 中的 WHERE + GROUP BY


五、聚合排序、聚合结果过滤、聚合性能优化

掌握了基本用法之后,如何让聚合结果更精确、查询更高效?这是从"会用"到"精通"的关键一步。

5.1 聚合排序

按桶的文档数排序
json 复制代码
GET /orders/_search
{
  "size": 0,
  "aggs": {
    "by_category": {
      "terms": {
        "field": "category",
        "size": 10,
        "order": { "_count": "asc" }
      }
    }
  }
}
按桶的 Key 排序(字母序)
json 复制代码
"order": { "_key": "asc" }
按子聚合的指标值排序
json 复制代码
GET /orders/_search
{
  "size": 0,
  "aggs": {
    "by_category": {
      "terms": {
        "field": "category",
        "size": 10,
        "order": { "total_sales": "desc" }
      },
      "aggs": {
        "total_sales": {
          "sum": { "field": "price" }
        }
      }
    }
  }
}

实战场景:运营想要看"哪个品类销售额最高",就用子聚合指标排序。这在构建排行榜类功能时非常实用。

5.2 聚合结果过滤

include/exclude------过滤 Terms 聚合的词条
json 复制代码
GET /orders/_search
{
  "size": 0,
  "aggs": {
    "selected_categories": {
      "terms": {
        "field": "category",
        "include": ["数码", "家居"]
      }
    }
  }
}

支持正则表达式过滤:

json 复制代码
"include": "数.*"
桶选择器(Bucket Selector)------按指标值过滤桶

桶选择器是一种管道聚合,根据父聚合的指标值来决定保留哪些桶。

需求:只保留销售额超过 500 的品类:

json 复制代码
GET /orders/_search
{
  "size": 0,
  "aggs": {
    "by_category": {
      "terms": { "field": "category", "size": 10 },
      "aggs": {
        "total_sales": {
          "sum": { "field": "price" }
        },
        "sales_filter": {
          "bucket_selector": {
            "buckets_path": {
              "totalSales": "total_sales"
            },
            "script": "params.totalSales > 500"
          }
        }
      }
    }
  }
}

参数说明

  • buckets_path:指定引用哪个子聚合的结果
  • script:过滤条件脚本,返回 true 的桶被保留

5.3 聚合作用域

global 聚合------突破 query 限制

默认情况下,聚合只在 query 匹配的文档范围内执行。如果需要在全量数据上做聚合(同时保留 query 的筛选),可以使用 global 聚合:

json 复制代码
GET /orders/_search
{
  "size": 0,
  "query": {
    "term": { "status": "已完成" }
  },
  "aggs": {
    "completed_stats": {
      "sum": { "field": "price" }
    },
    "all_orders_stats": {
      "global": {},
      "aggs": {
        "total": {
          "sum": { "field": "price" }
        }
      }
    }
  }
}

应用场景:数据看板上需要同时展示"已完成订单总额"和"全部订单总额"作为对比基准。

filter 聚合------为子聚合创建独立筛选
json 复制代码
GET /orders/_search
{
  "size": 0,
  "aggs": {
    "high_value_category": {
      "filter": {
        "range": { "price": { "gte": 1000 } }
      },
      "aggs": {
        "by_category": {
          "terms": { "field": "category" }
        }
      }
    }
  }
}

5.4 聚合性能优化

优化一:使用 doc_values 替 fielddata

这是最重要的聚合性能优化原则

  • doc_values:基于磁盘的列式存储,用于 keyword、数值、日期、布尔、IP、地理坐标类型,默认开启。内存友好,但查询时需要从磁盘读取。
  • fielddata:基于内存的数据结构,用于 text 字段。会占用大量 JVM 堆内存,可能导致 OOM。

黄金法则永远不要在 text 字段上做聚合! 应该使用 keyword 子字段:

json 复制代码
PUT /orders
{
  "mappings": {
    "properties": {
      "product_name": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword"
          }
        }
      }
    }
  }
}

聚合时使用 product_name.keyword 而非 product_name

优化二:设置 size: 0
json 复制代码
GET /orders/_search
{
  "size": 0,
  "aggs": { ... }
}

当只需要聚合结果时,设置 size: 0 跳过文档的获取和排序,大幅减少内存和 CPU 消耗。

优化三:控制 Terms 聚合的 shard_size
json 复制代码
"terms": {
  "field": "category",
  "size": 10,
  "shard_size": 20
}

shard_size 决定每个分片返回的候选桶数量。默认等于 size,如果数据分布不均匀,可能导致结果不准确。建议设为 size 的 1.5~2 倍。

优化四:善用 filter 聚合减少计算量

如果只需要部分数据的聚合,先用 filter 聚合缩小范围:

json 复制代码
"aggs": {
  "active_users": {
    "filter": { "term": { "status": "active" } },
    "aggs": {
      "by_region": {
        "terms": { "field": "region" }
      }
    }
  }
}
优化五:避免深度嵌套

嵌套层级越深,内存消耗呈指数增长。一般建议嵌套不超过 3 层。如果需要更复杂的分析,考虑以下方案:

  • 将中间结果写入临时索引,再做下一级聚合
  • 使用 Transform 功能预聚合数据
  • 在应用层分步聚合
优化六:合理使用 execution_hint

对于 Terms 聚合,可以提示执行策略:

json 复制代码
"terms": {
  "field": "category",
  "execution_hint": "map"
}
execution_hint 说明
map(默认) 使用 Map 结构,适合桶数量较少的场景
global_ordinals 使用全局序号,适合桶数量多且重复值多的场景
map_count 额外记录文档数,适合只需要排序的场景

架构师建议 :在实际生产中,如果 Terms 聚合的字段基数很大(如 user_id),global_ordinals 通常性能更好。如果基数较小(如 category),map 就足够了。


总结

本章系统地讲解了 ElasticSearch 聚合查询的核心知识,从基础概念到高级优化:

知识点 核心要点
聚合类型 桶聚合(分桶)+ 指标聚合(计算)+ 管道聚合(二次加工)
指标聚合 sum、avg、max、min、cardinality、stats
桶聚合 terms、range、date_range、histogram、date_histogram
嵌套聚合 桶内嵌指标、桶内嵌桶、多级嵌套
结果控制 排序、过滤(include/exclude)、桶选择器
作用域 global、filter
性能优化 doc_values 优先、size:0、控制 shard_size、避免深度嵌套

下一章预告 :第8章将深入 ElasticSearch 的集群架构,从节点角色到分片分配策略,从数据备份到跨集群复制,帮助你理解 ElasticSearch 在生产环境中的高可用设计。


如果这篇文章对你有帮助,欢迎 点赞 + 在看 + 转发 三连支持!有任何问题,欢迎在评论区留言讨论。

相关推荐
yunceqing1 小时前
从Excel调度到TMS平台:物流软件开发避坑清单
大数据·前端·网络·人工智能·excel·推荐算法
环球科讯1 小时前
广东省茂名市:普惠金融畅流通,建行助力商贸兴
大数据·人工智能
一切皆是因缘际会1 小时前
神经符号融合智能体
大数据·数据结构·人工智能·ai
TheRouter2 小时前
LLM 应用的Prompt 版本管理工程实践:从ad-hoc 字符串到生产级Prompt 仓库
大数据·elasticsearch·ai·prompt
环球科讯2 小时前
精准赋能实体企业——建设银行广东省茂名市分行金融活水浇灌砂石产业
大数据
小王毕业啦2 小时前
2012-2024年 上市公司-企业业务招待费数据 (xlsx+文献)
大数据·人工智能·数据挖掘·数据分析·社科数据·实证分析·经管数据
RoboWizard2 小时前
企业级SSD批量供货与品质一致性FAQ
大数据
杰克逊的日记2 小时前
kafka消息堆积了怎么处理
大数据·分布式·kafka
湘美书院--湘美谈教育2 小时前
湘美谈教育湘美书院考古教育系列:湖南史前文化序列整理
大数据·数据库·人工智能·深度学习·神经网络·机器学习