You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Elasticsearch如何高效实现三层条件聚合 优化API响应速度

Elasticsearch三层层级聚合性能优化方案

问题背景

需要实现三层维度的层级计数统计,统计逻辑为:

  • 统计全量文档中各topic的计数
  • 统计每个topic下各bucket的计数
  • 统计每个bucket下各key的计数
    当前环境基础信息:
  • 索引总大小141.7MB,总文档量8000~10000条
  • topic、bucket、key三个字段的独立取值量级均为1000~2000
  • 单条文档包含100~200组不同的topic、bucket、key取值,不同文档取值存在差异
    当前使用三层嵌套terms聚合实现,API场景下响应耗时3~4秒,无法满足低延迟要求,原查询语句如下:
GET index/doc/_search
{
  "aggs":{
    "topic_aggs": {
      "terms": {
        "field": "topic.keyword",
        "size":10000
      },
      "aggs" : {
        "bucket_aggs" : {
          "terms": {
            "field": "bucket.keyword",
            "size":10000
          },
          "aggs" : {
            "key_aggs": {
              "terms": {
                "field": "key.keyword",
                "size":10000
              }
            }
          }
        }
      }
    }
  }
}

慢查询核心原因

  • 每层terms聚合设置的size:10000远大于字段实际1000~2000的独立取值量,每一层聚合都会触发不必要的优先级队列排序开销,嵌套层级下开销会逐层放大
  • 未添加size:0参数,查询默认返回Top10原始文档,额外产生文档抓取、排序、序列化传输开销
  • 参与聚合的keyword字段未做预优化,首次查询时需要临时构建全局序数结构,增加查询阶段耗时

优化方案(按落地优先级排序)

所有优化落地后,查询耗时可降至100ms以内。

  • 匹配字段实际基数调整聚合size参数
    三个字段的独立取值上限为2000,将每层terms的size从10000调整为3000(预留后续取值增长空间),直接砍掉80%的无效排序计算和内存开销。
  • 关闭原始文档返回
    纯聚合统计场景不需要返回原始文档内容,在查询最外层添加"size": 0参数,跳过文档抓取、排序、序列化全流程的无效开销。
  • 为聚合字段开启全局序数预加载
    keyword字段做terms聚合依赖全局序数数据结构,默认在查询首次触发时才构建。对于固定参与聚合的字段,在mapping中开启eager_global_ordinals配置,索引刷新时提前构建好全局序数,查询时直接复用,消除查询阶段的结构构建开销。
    对应mapping配置:
    PUT index/_mapping/doc
    {
      "properties": {
        "topic": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "eager_global_ordinals": true
            }
          }
        },
        "bucket": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "eager_global_ordinals": true
            }
          }
        },
        "key": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "eager_global_ordinals": true
            }
          }
        }
      }
    }
    
    如果三个字段本身就是独立的keyword类型,直接给对应字段加eager_global_ordinals: true配置即可。
  • 添加低基数场景适配的执行hint
    对于基数在3000以内的keyword字段terms聚合,添加"execution_hint": "map"参数,直接使用内存哈希表做计数,跳过默认的全局序数遍历逻辑,小基数场景下性能提升明显。

优化后完整查询语句

GET index/doc/_search
{
  "size": 0,
  "aggs":{
    "topic_aggs": {
      "terms": {
        "field": "topic.keyword",
        "size": 3000,
        "execution_hint": "map"
      },
      "aggs" : {
        "bucket_aggs" : {
          "terms": {
            "field": "bucket.keyword",
            "size": 3000,
            "execution_hint": "map"
          },
          "aggs" : {
            "key_aggs": {
              "terms": {
                "field": "key.keyword",
                "size": 3000,
                "execution_hint": "map"
              }
            }
          }
        }
      }
    }
  }
}

注意:如果后续单字段独立取值超过5000,移除execution_hint: map配置即可,map执行模式在高基数场景下内存开销会高于默认的全局序数模式。

可选进阶优化(数据更新频率低场景)

如果索引数据为T+1更新或者小时级更新的静态数据,不需要实时聚合,可以通过定时任务提前计算好三层聚合结果,存入单独的结果索引,API直接查询预计算结果,响应耗时可降至10ms级别。

内容的提问来源于stack exchange,提问作者Robin Rawat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 17:48:18