📣 极限科技诚招搜索运维工程师(Elasticsearch/Easysearch)- 全职/北京 👉 : 立即申请加入

适用版本: 6.8-7.15

1. 错误异常的基本描述 #

Trying to create too many buckets. Must be less than or equal to: [bucketLimit] but was [size] 是 Elasticsearch 在执行聚合请求时抛出的异常,表示当前请求试图创建的 bucket 数量超过了集群配置的上限。

与普通的 terms 聚合在运行中期才触发限制不同,composite 聚合会在初始化阶段就检查 size 参数。只要请求中指定的 size 大于 search.max_buckets,请求会直接被拒绝,不会进入实际执行阶段。

常见现象 #

  • 发送 composite 聚合请求后立即返回 400 错误,响应体中包含 Trying to create too many buckets 异常信息。
  • 报错中的 was 值恰好等于请求体中 composite.aggs.size 的值。
  • 常见于业务方希望一次性拉取全量聚合结果,将 size 设置为几万甚至更大的场景。
  • 在 Kibana 或自定义 Dashboard 中配置大尺寸 composite 聚合时也容易触发此异常。

典型报错与异常栈 #

{
  "error": {
    "root_cause": [
      {
        "type": "too_many_buckets_exception",
        "reason": "Trying to create too many buckets. Must be less than or equal to: [65536] but was [100000]. This limit can be set by changing the [search.max_buckets] cluster level setting."
      }
    ],
    "type": "search_phase_execution_exception",
    "reason": "all shards failed",
    "failed_shards": [
      {
        "reason": {
          "type": "too_many_buckets_exception",
          "reason": "Trying to create too many buckets. Must be less than or equal to: [65536] but was [100000]. This limit can be set by changing the [search.max_buckets] cluster level setting."
        }
      }
    ]
  },
  "status": 400
}

2. 为什么会发生这个错误 #

composite 聚合的设计初衷是分批、流式地遍历所有 bucket,而不是一次性返回全部结果。Elasticsearch 在构造 composite 聚合器时,会在 compositesize 参数与集群级设置 search.max_buckets 之间做校验:

int bucketLimit = context.multiBucketConsumer().getLimit();
if (size > bucketLimit) {
    throw new MultiBucketConsumerService.TooManyBucketsException(
        "Trying to create too many buckets. Must be less than or equal" +
        " to: [" + bucketLimit + "] but was [" + size + "]. This limit can be set by changing the [" +
        MAX_BUCKET_SETTING.getKey() + "] cluster level setting.", bucketLimit
    );
}

常见触发原因包括:

  • 误将 composite 聚合当作全量导出接口:composite 聚合的正确用法是通过 after_key 分页迭代,而不是一次性设置超大 size
  • search.max_buckets 设置过小:默认值(如 65536)对某些业务场景可能偏低,但这通常是合理的安全阈值。
  • 业务方未评估内存开销:每个 bucket 都会占用协调节点和数据节点的内存,无限制地拉取 bucket 会导致 JVM 堆压力骤增。
  • 从 terms 聚合迁移时未调整用法:terms 聚合通过 size 控制返回数量,而 composite 聚合的 size 是单页大小,两者语义不同。

3. 如何排查和解决这个异常 #

建议按以下顺序排查:

  1. 确认异常对应的请求:从报错信息中提取 was 后的数值,即为请求中 size 的值。
  2. 查看集群当前限制:执行以下命令获取 search.max_buckets 的当前值:
    GET _cluster/settings?include_defaults=true&flat_settings=true
    # 或精准查询
    GET _cluster/settings?filter_path=*.search.max_buckets
    
  3. 检查请求中的 size 参数:确认 composite 聚合中 size 的设置是否合理。
  4. 评估数据规模:通过以下查询估算实际需要的 bucket 数量:
    GET /your_index/_search
    {
      "size": 0,
      "aggs": {
        "distinct_count": {
          "cardinality": {
            "field": "your_composite_field"
          }
        }
      }
    }
    

排查时需要注意的问题 #

  • search.max_buckets集群级设置,修改后会影响所有聚合请求,不宜随意调大。
  • composite 聚合的 size单页大小,不是总 bucket 数上限。
  • 如果报错中的 was 值远大于 bucketLimit,通常说明请求设计有问题,而不是限制有问题。

4. 如何解决这个错误 #

常用修复思路 #

方案一:降低 composite 聚合的 size,使用 after_key 分页

这是最推荐的做法。将 size 调整到 search.max_buckets 以内,然后通过 after_key 迭代获取全部结果:

# 第一页
GET /your_index/_search
{
  "size": 0,
  "aggs": {
    "my_buckets": {
      "composite": {
        "size": 1000,
        "sources": [
          { "keyword_field": { "terms": { "field": "category.keyword" } } }
        ]
      }
    }
  }
}

# 后续页:使用上一页返回的 after_key
GET /your_index/_search
{
  "size": 0,
  "aggs": {
    "my_buckets": {
      "composite": {
        "size": 1000,
        "sources": [
          { "keyword_field": { "terms": { "field": "category.keyword" } } }
        ],
        "after": {
          "keyword_field": "上一个 bucket 的 key 值"
        }
      }
    }
  }
}

方案二:临时调高 search.max_buckets(需谨慎评估)

# 临时设置(重启后失效)
PUT _cluster/settings
{
  "transient": {
    "search.max_buckets": 100000
  }
}

# 持久化设置
PUT _cluster/settings
{
  "persistent": {
    "search.max_buckets": 100000
  }
}

注意: 调高此限制前,务必评估协调节点和数据节点的 JVM 堆内存是否充足,避免引发 OOM。

后续注意事项与推荐建议 #

  • 封装分页 SDK:在业务层封装 composite 聚合的分页迭代逻辑,避免业务代码直接设置超大 size
  • search.max_buckets 视为安全阈值:该限制的存在是为了防止单个请求耗尽集群内存,不应被随意放大。
  • 监控大聚合请求:通过慢查询日志或 INFINI Gateway 识别 size 过大的聚合请求,及时干预。
  • 优先使用 composite 而非 terms 做深度分页聚合:composite 天然支持游标分页,更适合大规模遍历场景。

借助 INFINI 产品提升排障效率 #

  • INFINI Console 可查看集群的聚合请求画像、慢查询趋势和节点内存使用情况,帮助判断是否存在异常的大聚合请求。
  • INFINI Gateway 可部署在 Elasticsearch 前面,对 size 过大的 composite 聚合请求进行拦截、限流或告警,防止单个请求拖垮整个集群。

5. 小结 #

Trying to create too many buckets 在 composite 聚合场景中,根因通常是请求的单页 size 超过了 search.max_buckets 限制。修复重点不在于调高限制,而在于正确使用 composite 聚合的分页机制:将 size 控制在合理范围内,通过 after_key 迭代获取全量数据。

只要建立规范的分页封装、将 search.max_buckets 视为保护阈值而非可随意调整的参数,大多数此类异常都可以从设计层面避免。

相关错误 #

附:日志上下文 #

下面保留当前页面中的源码片段,便于结合异常调用栈定位问题:

int bucketLimit = context.multiBucketConsumer().getLimit();
if (size > bucketLimit) {
    throw new MultiBucketConsumerService.TooManyBucketsException("Trying to create too many buckets. Must be less than or equal" +
    " to: [" + bucketLimit + "] but was [" + size + "]. This limit can be set by changing the [" + MAX_BUCKET_SETTING.getKey() +
    "] cluster level setting.", bucketLimit);
}