适用版本: 6.8-8.9
1. 错误说明 #
Failed to build aggregation [aggregator_name] 表示 Elasticsearch 在执行完聚合计算后、准备将结果写入响应时发生了异常。该错误并非发生在 DSL 解析或查询执行阶段,而是在结果构造阶段(buildTopLevel)抛出的,因此排查思路与普通查询错误有所不同。
常见现象 #
- 聚合请求返回
500或503状态码,响应体中包含Failed to build aggregation错误信息。 - 部分聚合可以正常返回,但某个特定聚合器在结果构造阶段失败,导致整个响应中断。
- 在 Elasticsearch 服务端日志中可以看到
AggregationExecutionException,并附带具体聚合器名称。 - 如果使用了多层嵌套聚合或 pipeline 聚合,错误可能只在特定数据条件下触发,难以稳定复现。
典型报错 #
{
"error": {
"root_cause": [
{
"type": "aggregation_execution_exception",
"reason": "Failed to build aggregation [my_agg]"
}
],
"type": "search_phase_execution_exception",
"reason": "all shards failed",
"failed_shards": [
{
"reason": {
"type": "aggregation_execution_exception",
"reason": "Failed to build aggregation [my_agg]",
"caused_by": {
"type": "io_exception",
"reason": "No space left on device"
}
}
}
]
}
}
2. 原因分析 #
从 Elasticsearch 源码来看,聚合结果构建的核心逻辑位于 SearchPhaseController 中,在聚合执行完成后遍历所有聚合器并调用 buildTopLevel():
for (Aggregator aggregator : context.aggregations().aggregators()) {
try {
aggregator.postCollection();
aggregations.add(aggregator.buildTopLevel());
} catch (IOException e) {
throw new AggregationExecutionException(
"Failed to build aggregation [" + aggregator.name() + "]", e);
}
aggregator.releaseAggregations();
}
常见触发原因包括:
- 聚合器内部状态不完整:聚合执行过程中累计的中间状态因异常数据而损坏,导致无法正确生成顶层结果对象。
- 字段映射或数据类型异常:聚合所依赖的字段 mapping 发生变化(如字段类型变更、doc values 被禁用),而已有数据与新 mapping 不兼容。
- 脚本或 runtime field 错误:在聚合中使用了脚本或 runtime field,脚本逻辑在序列化阶段抛出
IOException或ClassCastException。 - 插件型聚合器兼容性问题:使用了第三方插件提供的聚合类型,插件版本与当前 Elasticsearch 版本不兼容,在结果序列化时失败。
- 嵌套或 pipeline 聚合层级过深:多层嵌套聚合或连续 pipeline 聚合(如
bucket_sort、cumulative_sum)在中间桶数据为空或格式异常时,下游聚合在构建结果时失败。 - 磁盘空间不足或 IO 异常:
buildTopLevel()阶段可能涉及中间文件写入或网络 IO,若磁盘满或发生 IO 错误,会被包装成当前异常。
3. 解决方案 #
3.1 定位具体聚合器 #
首先从异常信息中提取聚合器名称,将原始 DSL 逐步精简,使用最小可复现的聚合定义进行隔离测试:
{
"size": 0,
"aggs": {
"problem_agg": {
"terms": {
"field": "your_field.keyword",
"size": 10
}
}
}
}
3.2 检查字段 mapping 与数据兼容性 #
使用以下步骤确认字段状态:
# 查看索引 mapping
GET /your_index/_mapping/field/your_field
# 查看字段是否存在 doc_values 被禁用的情况
GET /your_index/_mapping
# 检查是否有 runtime field 定义冲突
GET /your_index/_mapping?include_runtime=true
3.3 修复脚本或 runtime field #
如果聚合依赖脚本,验证脚本逻辑:
{
"size": 0,
"runtime_mappings": {
"test_script_field": {
"type": "long",
"script": {
"source": "emit(doc['price'].value ?: 0)"
}
}
},
"aggs": {
"test_agg": {
"terms": {
"field": "test_script_field"
}
}
}
}
3.4 处理嵌套或 pipeline 聚合 #
逐层拆解复杂聚合,定位具体失败层级:
{
"size": 0,
"aggs": {
"level1": {
"terms": {
"field": "category.keyword"
},
"aggs": {
"level2": {
"avg": {
"field": "price"
}
}
}
}
}
}
先去掉 level2,确认 level1 是否正常;再逐步加回下游聚合,直到错误复现。
3.5 检查节点磁盘与 IO 状态 #
# 检查磁盘使用率
GET /_cat/allocation?v
# 检查节点信息
GET /_cat/nodes?v&h=name,disk.avail,disk.total,disk.percent
4. 预防措施 #
- 变更字段 mapping 前做好兼容性评估:避免直接修改已有字段类型,优先使用别名或新增字段的方式平滑迁移。
- 脚本和 runtime field 上线前充分测试:在开发或测试环境验证脚本逻辑对边界数据(null、空字符串、异常类型)的处理能力。
- 控制聚合复杂度:避免过深的嵌套聚合层级和过多的 bucket 数量,必要时使用
composite聚合替代深度嵌套。 - 监控磁盘使用率与 IO 异常:建立磁盘水位告警,避免因磁盘满导致聚合结果写入失败。
- 定期巡检插件兼容性:升级 Elasticsearch 版本时同步确认第三方插件是否提供对应版本的兼容包。
5. 相关错误 #
- failed-to-build-top-level-pipeline-aggregators-how-to-solve-this-elasticsearch-exception
- failed-to-build-inner-hits-how-to-solve-this-elasticsearch-exception
- aggregation-execution-exception-how-to-solve-this-elasticsearch-exception
附:日志上下文 #
for (Aggregator aggregator : context.aggregations().aggregators()) {
try {
aggregator.postCollection();
aggregations.add(aggregator.buildTopLevel());
} catch (IOException e) {
throw new AggregationExecutionException(
"Failed to build aggregation [" + aggregator.name() + "]", e);
}
// release the aggregator to claim the used bytes as we don't need it anymore
aggregator.releaseAggregations();
}
context.queryResult().aggregations(InternalAggregations.from(aggregations));





