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

适用版本: 8.4-8.9

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

Failed to merge shard results 通常出现在 Elasticsearch 执行搜索、聚合或排序请求的归并阶段。请求会先在各个分片上独立执行,协调节点再把每个分片返回的结果做 reduce、排序、聚合合并;如果某个分片返回异常结果、归并过程中触发 IO 问题,或者聚合器在 merge 阶段无法处理分片返回值,就可能抛出这个异常。

从源码片段看,这个错误最终由 AggregationExecutionException("Failed to merge shard results") 抛出,说明问题更偏向聚合结果归并、事务结果合并或 reduce 阶段异常,而不是普通的连接报错或快照故障。排查时应优先围绕“是哪一个查询、哪一个聚合、哪一个分片返回了异常结果”建立证据链。

常见现象 #

  • 在执行带聚合的 _search 请求时,接口直接返回 search_phase_execution_exceptionaggregation_execution_exceptionall shards failed,而不是正常的查询结果。
  • 某些查询只在特定索引、特定时间范围或特定聚合条件下失败,说明问题可能集中在少数分片、少量脏数据或特定字段值上。
  • 如果问题与资源有关,查询响应时间通常会先明显变慢,然后在协调节点归并结果时失败;日志中可能同时出现内存压力、断路器、线程池阻塞或 IO 异常信息。
  • 从业务角度看,最直接的表现通常是报表查询失败、大盘聚合结果不可用、下钻分析中断,或者同一条 DSL 在测试环境正常、生产环境偶发失败。

典型报错与异常栈 #

这类错误经常与下面这些异常一起出现:

  • search_phase_execution_exception
  • aggregation_execution_exception
  • all shards failed
  • CircuitBreakingException
  • IOException
  • illegal_argument_exception

如果异常来自聚合 reduce 阶段,常见日志形态通常类似下面这样:

org.elasticsearch.search.aggregations.AggregationExecutionException: Failed to merge shard results
Caused by: java.io.IOException
at org.elasticsearch.search.aggregations...
at org.elasticsearch.action.search.SearchPhaseController...

如果是查询执行阶段先出现分片级错误,再在协调节点统一抛出,也可能看到类似返回:

{
    "error": {
        "type": "search_phase_execution_exception",
        "reason": "all shards failed",
        "failed_shards": [
            {
                "shard": 0,
                "index": "logs-2026.03.31",
                "reason": {
                    "type": "aggregation_execution_exception",
                    "reason": "Failed to merge shard results"
                }
            }
        ]
    },
    "status": 500
}

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

这个错误本质上是“分片本地执行结果生成了,但协调节点在归并结果时失败了”。和普通查询语法错误不同,它通常发生在请求已经下发到多个分片之后,因此更容易和数据分布、聚合器状态、节点资源、字段类型不一致等问题相关。

常见原因通常包括:

  • 某些分片上的字段映射并不一致,例如同名字段在不同索引或不同分片上的类型不同,导致聚合 reduce 时无法合并结果。
  • 聚合查询本身过于复杂,例如嵌套聚合层级过深、基数过大、脚本聚合开销过高,导致归并阶段内存占用过高或内部状态损坏。
  • 分片所在节点存在 IO 异常、磁盘抖动、JVM 内存压力、断路器触发或线程池拥塞,造成分片返回不完整结果或 reduce 过程被中断。
  • 查询命中了异常数据,例如字段值格式不符合预期、脚本处理异常、doc values 损坏或 segment 文件存在底层读取问题。
  • 集群在查询执行过程中正好发生分片迁移、节点重启、主从切换,导致协调节点接收到的部分分片结果不完整或上下文失效。

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

建议按照“先锁定请求,再定位失败分片,最后判断是 DSL、数据还是节点资源问题”的顺序排查:

  1. 先保留完整 DSL,请求参数里尤其要记录索引范围、聚合结构、排序字段、脚本和时间范围。
  2. 使用同一条查询在较小时间范围或单个索引上重试,判断问题是全局性的,还是集中在少数索引或分片。
  3. 查看错误返回中的 failed_shards、分片编号和索引名,尽量先锁定是哪一个分片或哪一类索引触发失败。
  4. 再检查字段映射、节点资源、线程池、断路器和慢日志,判断是数据/DSL 问题还是节点层故障。
  5. 如果问题与聚合或脚本强相关,优先简化 DSL,逐步移除聚合层级、脚本或排序条件,缩小触发面。

相关 Elasticsearch API 及调用说明 #

下面这些接口最适合排查 Failed to merge shard results。建议从“复现请求本身”开始,再看分片、字段映射和节点资源。

1. 直接复现 _search 请求 #

用于确认到底是哪一条 DSL 触发了归并失败。

curl -X GET "http://localhost:9200/my_index/_search?pretty" \
    -H 'Content-Type: application/json' \
    -d '{
        "size": 0,
        "query": {
            "range": {
                "@timestamp": {
                    "gte": "now-15m",
                    "lte": "now"
                }
            }
        },
        "aggs": {
            "top_users": {
                "terms": {
                    "field": "user.keyword"
                }
            }
        }
    }'

建议先把原始请求缩小到最小可复现版本,例如只保留一个聚合、一个过滤条件。这样更容易看出是哪个聚合或字段导致 merge 失败。

2. 查看错误返回里的 failed_shards #

如果 _search 已经返回了异常,就重点关注返回体中的 failed_shardsindexshardreason。这一步不需要额外 API,但它决定了后面要查哪个索引、哪个分片。

3. 查看字段映射 #

用于确认参与聚合或排序的字段,在不同索引上是否类型一致。

curl -X GET "http://localhost:9200/my_index/_mapping?pretty"

如果查询跨多个索引:

curl -X GET "http://localhost:9200/logs-*/_mapping/field/user.keyword?pretty"

重点关注:

  • 同名字段是否在不同索引中映射成不同类型。
  • 聚合字段是否可聚合,是否开启了 doc_values
  • 是否误把 text 字段直接用于 terms 或排序。

4. 校验查询是否合法 #

用于在不真正执行完整搜索的情况下检查 DSL 是否存在明显问题。

curl -X GET "http://localhost:9200/my_index/_validate/query?pretty&explain=true" \
    -H 'Content-Type: application/json' \
    -d '{
        "query": {
            "range": {
                "@timestamp": {
                    "gte": "now-15m",
                    "lte": "now"
                }
            }
        }
    }'

它不能直接发现所有 reduce 阶段问题,但能先排掉明显的 DSL 构造错误。

5. 查看集群健康状态 #

用于确认查询失败时,集群是否正处于分片异常、节点抖动或迁移状态。

curl -X GET "http://localhost:9200/_cluster/health?pretty"

重点关注:

  • status 是否为 yellowred
  • relocating_shardsinitializing_shards 是否异常偏高。
  • 是否存在大量未分配分片。

6. 查看分片分布与状态 #

用于确认失败查询命中的索引,其分片是否集中在某个异常节点上。

curl -X GET "http://localhost:9200/_cat/shards/my_index?v"

如果查询跨多个索引,也可以先查看全部:

curl -X GET "http://localhost:9200/_cat/shards?v"

重点关注:

  • 失败分片是否反复落在同一节点。
  • 是否存在 RELOCATINGUNASSIGNEDINITIALIZING 状态。
  • 主分片是否正常 STARTED

7. 查看节点资源状态 #

用于判断归并失败是否与内存、断路器、线程池或 IO 压力有关。

curl -X GET "http://localhost:9200/_nodes/stats/jvm,fs,thread_pool,breaker?pretty"

重点关注:

  • JVM 堆使用率是否持续过高。
  • breaker 是否频繁触发。
  • searchwritesearch_throttled 等线程池是否积压。
  • 磁盘和 IO 是否只在个别节点异常。

8. 查看热点线程 #

如果怀疑查询执行或 reduce 阶段被卡住,可以查看热点线程。

curl -X GET "http://localhost:9200/_nodes/hot_threads?pretty"

如果输出里长期出现聚合计算、脚本执行或 segment 读取相关堆栈,说明问题很可能不只是数据错误,还可能有计算热点或底层 IO 瓶颈。

9. 查看当前任务 #

用于判断是否存在长时间运行的搜索或聚合任务。

curl -X GET "http://localhost:9200/_tasks?pretty&actions=*search"

如果查询长期不返回、最终在 merge 阶段报错,可以结合这个接口判断是请求本身过重,还是底层节点已经进入阻塞状态。

排查时需要注意的问题 #

  • 不要只看协调节点上的一句 Failed to merge shard results,更关键的是找出最先失败的那个分片异常。
  • 如果请求跨多个日期索引,建议逐个缩小索引范围复现,快速定位是否是某个历史索引中的脏数据或错误 mapping 导致。
  • 如果查询里使用了脚本聚合、pipeline aggregation、嵌套聚合或高基数 terms 聚合,优先怀疑请求复杂度和字段数据质量,而不是先怀疑网络。

4. 如何解决这个错误 #

常用修复思路 #

  • 如果根因是字段映射不一致,优先统一 mapping,再通过重建索引或调整查询范围规避异常索引。
  • 如果根因是聚合 DSL 过重,优先降低聚合基数、减少嵌套层级、缩小时间范围,必要时拆分查询批次。
  • 如果根因是脚本或脏数据,先修复脚本逻辑、增加空值保护或数据清洗规则,再重新执行查询。
  • 如果根因是节点资源瓶颈,先恢复节点稳定性,必要时扩容协调节点或数据节点,并限制高开销聚合请求。

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

  • 为高频聚合查询建立 DSL 审核机制,避免未经评估的高基数聚合、脚本聚合直接进入生产环境。
  • 在索引模板和字段设计阶段统一规范字段类型,减少跨索引查询时的 mapping 漂移。
  • 为慢查询、断路器、热点线程和查询失败率建立告警,尽量在 merge 阶段报错之前发现资源风险。

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

  • INFINI Console 适合从节点资源、索引状态、慢查询和错误趋势几个维度一起看,快速判断这是单个索引的数据问题,还是全局性的查询/资源瓶颈。
  • INFINI Gateway 适合放在 Elasticsearch 前面统一采样查询 DSL、识别高风险聚合请求,并对异常请求做限流、熔断或审计。
  • 如果这类问题反复出现,建议把失败 DSL、来源应用、索引范围、耗时和错误类型统一接入观测平台,形成“问题查询画像”。

5. 小结 #

Failed to merge shard results 的关键不在“merge”这几个字,而在于它意味着分片已经执行到一定阶段,但协调节点在归并时发现结果无法继续处理。真正的根因往往藏在字段映射不一致、聚合 DSL 过重、个别分片异常数据、节点资源不足或底层 IO 问题里。

处理这类问题时,最有效的办法是先拿到原始 DSL,再通过 failed_shards、mapping、分片状态和节点资源逐步缩小范围。只要把“查询复现 + 分片定位 + 资源核查”这套方法固定下来,这类异常通常都能比盲目猜测更快定位,也更适合借助 INFINI Console 和 INFINI Gateway 做持续治理。

附:日志上下文 #

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

    if (profiling) {
        ramBytesSum.addAndGet(p.ramBytesUsed());
    }
    transactionStore.merge(p);
} catch (IOException e) {
    throw new AggregationExecutionException("Failed to merge shard results"; e);
} finally {
    Releasables.close(p);
}
});