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

适用版本: 7.12-8.9

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

missing shard 是 Elasticsearch 在执行 downsample(下采样) 任务时抛出的异常,表示协调节点在汇总各分片返回结果时,发现 shardsResponses 数组中某个位置本应有分片响应,实际却拿到了 null。这个错误不是普通搜索里的"找不到分片",而是后台任务在遍历分片级执行结果时,发现某个分片根本没有返回有效响应。

从源码来看,该异常发生在 DownsampleIndexerActioninnerReduce 阶段:

for (int i = 0; i < shardsResponses.length(); i++) {
    Object shardResponse = shardsResponses.get(i);
    if (shardResponse == null) {
        throw new ElasticsearchException("missing shard");
    }
    ...
}

这意味着 downsample 任务在归并分片结果时,期望收到 N 个分片响应,但实际响应数组长度不足,或某个位置为 null

常见现象 #

  • 执行 downsample API(_downsample)时,任务在 reduce 阶段失败,返回 ElasticsearchException: missing shard
  • 在 Kibana 或应用中触发 downsample 任务后,任务状态一直停留在 failed,且无法自动恢复。
  • Elasticsearch 服务端日志中可以看到 downsample 任务先成功执行了部分分片,随后因为某个空响应而终止。
  • 某些场景下会伴随节点重启、任务取消、分片执行异常或 task cancelled 等日志信息。
  • 如果是通过 ILM(索引生命周期管理)自动触发 downsample,可能导致整个生命周期步骤卡住,索引无法继续 rollover 或 downsample。

典型报错与异常栈 #

该异常的典型日志形态如下:

ElasticsearchException: missing shard
    at org.elasticsearch.action.downsample.DownsampleIndexerAction$InnerReduceContext$1.consume(...)
    at org.elasticsearch.action.downsample.DownsampleIndexerAction$ShardDownsampleResponse[]

通过 API 触发的 downsample 请求,返回体通常如下:

{
    "error": {
        "root_cause": [
            {
                "type": "elasticsearch_exception",
                "reason": "missing shard"
            }
        ],
        "type": "elasticsearch_exception",
        "reason": "missing shard",
        "status": 500
    }
}

另一种常见形态(伴随任务失败日志):

[2024-01-15T10:23:45,123][WARN ][o.e.a.d.DownsampleIndexerAction] [node-1] failed to execute downsample for index [source_index]
org.elasticsearch.ElasticsearchException: missing shard
    at org.elasticsearch.action.downsample.DownsampleIndexerAction.innerReduce(...)
    at org.elasticsearch.action.downsample.TransportDownsampleAction.lambda$executeDownsample$5(...)

如果问题发生在任务执行的更早期(分片级执行失败),还可能看到类似:

Caused by: org.elasticsearch.ElasticsearchException: missing shard
Caused by: java.util.concurrent.CancellationException
Caused by: org.elasticsearch.transport.NodeDisconnectedException

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

Elasticsearch 的 downsample 是一个多阶段任务:首先在各个分片上并行执行 ShardDownsampleAction,然后将各分片的结果汇总到协调节点进行 reduce。协调节点在遍历 shardsResponses 数组时,期望每个位置都有有效的响应对象(要么是 ShardDownsampleResponse,要么是 Exception),如果某个位置是 null,就会抛出 missing shard

常见原因通常包括:

  • 分片执行失败但未正确封装异常:某个分片上的 downsample 执行失败,但异常没有被正确放入 shardsResponses 数组,而是直接丢失了,导致该位置为 null
  • 节点在任务执行期间离线:downsample 任务执行过程中,持有目标分片的节点重启、崩溃或网络断开,导致该分片无法返回结果,且协调端未收到有效的异常对象。
  • 任务被取消但未清理响应槽位:如果 downsample 任务被手动取消(通过 _tasks API)或超时取消,某些分片的响应槽位可能未被正确填充。
  • 并发竞态条件:在极端情况下,内部 bug 或并发竞态可能导致 shardsResponses 数组在填充完成前就被读取,或某个分片的响应被错误地覆盖为 null
  • 分片迁移与任务执行重叠:downsample 任务执行期间,恰好发生分片重分配、迁移或恢复操作,导致目标分片短暂不可用。
  • 索引在任务执行期间被删除或关闭:如果 downsample 的源索引或目标索引在任务执行期间状态发生变化(如被关闭、删除),分片执行会失败且可能丢失响应。

源码中的关键逻辑:

// DownsampleIndexerAction 中的 reduce 循环
for (int i = 0; i < shardsResponses.length(); i++) {
    Object shardResponse = shardsResponses.get(i);
    if (shardResponse == null) {
        // 如果某个分片响应为 null,直接抛异常
        throw new ElasticsearchException("missing shard");
    } else if (shardResponse instanceof DownsampleIndexerAction.ShardDownsampleResponse r) {
        successfulShards++;
        numIndexed += r.getNumIndexed();
    } else if (shardResponse instanceof Exception e) {
        // 如果分片返回的是异常,也重新抛出
        throw new ElasticsearchException(e);
    }
}

这说明 shardsResponses 数组中的每个元素必须是以下三种情况之一:

  1. 一个有效的 ShardDownsampleResponse 对象
  2. 一个 Exception 对象
  3. 不能是 null(否则抛 missing shard

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

建议按"先确认任务状态,再定位失败分片,最后判断是节点问题还是数据问题"的顺序处理:

  1. 先确认 downsample 任务的完整错误信息,包括任务 ID、源索引、目标索引、失败时间点和异常堆栈。
  2. 查看任务执行期间各分片的执行状态,确认是否有分片失败、节点离线或任务被取消。
  3. 检查源索引和目标索引的分片分配状态、节点健康度和集群状态,确认没有未分配或异常的分片。
  4. 查看 Elasticsearch 日志中 downsample 相关的 WARNERROR 级别日志,重点关注 DownsampleIndexerActionTransportDownsampleAction 等类。
  5. 如果是偶发问题,检查任务执行期间是否有节点重启、分片迁移、集群状态变更或网络抖动。

相关 Elasticsearch API 及调用说明 #

下面这些接口最适合排查 missing shard 错误。建议从复现 downsample 请求开始,再逐步定位分片、节点和任务状态。

1. 复现 downsample 请求 #

用于确认是哪条 downsample 任务触发了 missing shard 错误。

# 触发 downsample 操作
curl -X POST "http://localhost:9200/_downsample/source_index/_downsample/target_index?pretty" \
    -H 'Content-Type: application/json' \
    -d '{
        "fixed_interval": "1h",
        "dimensions": [
            { "field": "@timestamp" },
            { "field": "host.keyword" }
        ],
        "metrics": [
            {
                "field": "response_time",
                "metrics": ["min", "max", "sum", "value_count"]
            }
        ]
    }'

如果请求直接返回 missing shard,则说明问题在分片执行阶段就已经发生。

2. 查看正在运行的任务 #

用于确认 downsample 任务是否还在执行,或者是否已经失败。

# 查看所有正在运行的任务
curl -X GET "http://localhost:9200/_tasks?pretty&detailed=true&actions=*downsample*"

# 查看特定任务的状态
curl -X GET "http://localhost:9200/_tasks/<task_id>?pretty"

重点关注:

  • 任务状态是否为 FAILEDCANCELLED
  • 是否有 cancellationnode_disconnected 相关原因。
  • 任务执行时间是否异常短(可能说明分片一开始就失败了)。

3. 查看源索引的分片状态 #

用于确认 downsample 源索引的分片是否全部可用。

# 查看源索引的分片分配状态
curl -X GET "http://localhost:9200/_cat/shards/source_index?v"

# 查看分片详细信息
curl -X GET "http://localhost:9200/_cat/shards/source_index?h=index,shard,prirep,state,docs,store,ip,node&v"

重点关注:

  • 所有主分片是否都处于 STARTED 状态。
  • 是否存在 UNASSIGNEDINITIALIZINGRELOCATING 状态的分片。
  • 分片是否分布在多个节点上,是否有节点负载不均。

4. 使用 allocation explain 查看分片分配原因 #

如果源索引有分片未分配,可以通过此 API 查看原因。

curl -X GET "http://localhost:9200/_cluster/allocation/explain?pretty" \
    -H 'Content-Type: application/json' \
    -d '{
        "index": "source_index",
        "shard": 0,
        "primary": true
    }'

5. 查看集群健康状态 #

用于确认 downsample 任务执行期间集群是否稳定。

# 查看集群整体健康状态
curl -X GET "http://localhost:9200/_cluster/health?pretty"

# 查看源索引的健康状态
curl -X GET "http://localhost:9200/_cluster/health/source_index?pretty"

重点关注:

  • status 是否为 greenyellow
  • unassigned_shards 是否大于 0
  • relocating_shardsinitializing_shards 是否异常偏高(说明有分片在迁移)。

6. 查看节点状态和日志 #

用于确认是否有节点在 downsample 任务执行期间离线或异常。

# 查看所有节点状态
curl -X GET "http://localhost:9200/_cat/nodes?v"

# 查看节点统计信息(JVM、线程池等)
curl -X GET "http://localhost:9200/_nodes/stats/jvm,thread_pool?pretty"

同时查看 Elasticsearch 日志:

# 查看 downsample 相关的错误日志
tail -n 1000 /var/log/elasticsearch/elasticsearch.log | grep -E "downsample|DownsampleIndexerAction|missing shard"

# 查看任务执行时间窗口内的节点断开日志
tail -n 1000 /var/log/elasticsearch/elasticsearch.log | grep -E "disconnected|node_left|NodeDisconnected"

7. 查看目标索引是否存在异常 #

downsample 的目标索引如果已经存在或部分创建,也可能导致问题。

# 检查目标索引是否存在
curl -X GET "http://localhost:9200/target_index?pretty"

# 如果目标索引存在但状态异常,可以删除后重试
curl -X DELETE "http://localhost:9200/target_index?pretty"

8. 在测试环境验证 #

如果生产环境难以排查,可以在测试环境使用相同的数据和结构进行验证:

# 在测试环境创建相同的源索引和 downsample 配置
curl -X POST "http://localhost:9200/_downsample/test_source/_downsample/test_target?pretty" \
    -H 'Content-Type: application/json' \
    -d '{
        "fixed_interval": "1h",
        "dimensions": [
            { "field": "@timestamp" }
        ],
        "metrics": []
    }'

排查时需要注意的问题 #

  • 不要只看 missing shard 这个报错本身,它是结果而不是原因。需要结合任务日志、节点日志和分片状态一起分析,找出哪个分片没有返回结果。
  • 关注任务执行时间窗口:确认 downsample 任务失败时,是否有节点重启、分片迁移、集群重启等操作发生。
  • 注意 downsample 的配置:如果 fixed_intervaldimensions 配置有问题,可能导致某些分片执行失败,但异常信息丢失。
  • 区分数据节点和协调节点missing shard 是在协调节点的 reduce 阶段抛出的,但根因可能在数据节点的分片执行阶段。

4. 如何解决这个错误 #

常用修复思路 #

方案一:确保分片和节点健康后重试(推荐) #

这是最常用也最安全的修复方案。先确认所有分片都正常,再重新执行 downsample。

# 1. 确认集群健康
curl -X GET "http://localhost:9200/_cluster/health/source_index?wait_for_status=yellow&timeout=30s"

# 2. 确认所有分片都已分配
curl -X GET "http://localhost:9200/_cat/shards/source_index?v" | grep -v "STARTED"

# 3. 如果目标索引已存在(部分创建),先删除
curl -X DELETE "http://localhost:9200/target_index?pretty"

# 4. 重新执行 downsample
curl -X POST "http://localhost:9200/_downsample/source_index/_downsample/target_index?pretty" \
    -H 'Content-Type: application/json' \
    -d '{
        "fixed_interval": "1h",
        "dimensions": [{ "field": "@timestamp" }],
        "metrics": []
    }'

方案二:检查并修复分片分配问题 #

如果源索引有未分配的分片,需要先解决分片分配问题。

# 1. 查看未分配分片的原因
curl -X GET "http://localhost:9200/_cluster/allocation/explain?pretty" \
    -H 'Content-Type: application/json' \
    -d '{"index": "source_index", "shard": 0, "primary": true}'

# 2. 如果是磁盘水位问题,清理磁盘或调整水位设置
curl -X PUT "http://localhost:9200/_cluster/settings?pretty" \
    -H 'Content-Type: application/json' \
    -d '{
        "persistent": {
            "cluster.routing.allocation.disk.watermark.low": "85%",
            "cluster.routing.allocation.disk.watermark.high": "90%"
        }
    }'

# 3. 如果是节点过滤问题,调整分配感知设置
curl -X PUT "http://localhost:9200/source_index/_settings?pretty" \
    -H 'Content-Type: application/json' \
    -d '{
        "index.routing.allocation.enable": "all"
    }'

# 4. 重试失败的分配
curl -X POST "http://localhost:9200/_cluster/reroute?retry_failed=true"

方案三:调整 downsample 配置 #

如果 downsample 配置过于复杂,可能导致某些分片执行失败。可以尝试简化配置。

// 复杂配置(可能触发问题)
{
    "fixed_interval": "1m",
    "dimensions": [
        { "field": "@timestamp" },
        { "field": "host.keyword" },
        { "field": "path.keyword" },
        { "field": "user.keyword" }
    ],
    "metrics": [
        { "field": "response_time", "metrics": ["min", "max", "sum", "avg", "value_count", "percentiles"] },
        { "field": "bytes", "metrics": ["sum", "avg"] }
    ]
}

// 简化配置(降低单分片复杂度)
{
    "fixed_interval": "1h",
    "dimensions": [
        { "field": "@timestamp" }
    ],
    "metrics": [
        { "field": "response_time", "metrics": ["min", "max", "sum"] }
    ]
}

方案四:升级到修复版本 #

如果问题是由 Elasticsearch 的内部 bug 引起的,检查是否有相关修复版本。

# 查看当前版本
curl -X GET "http://localhost:9200/?pretty"

# 查看版本信息
curl -X GET "http://localhost:9200/_nodes/plugins?pretty"

已知在 7.12-8.9 版本范围内,missing shard 错误可能与 downsample 的异常处理机制有关。如果确认是版本 bug,建议升级到包含修复的版本(查看 Elasticsearch Release Notes)。

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

  • 建立 downsample 任务监控:监控 downsample 任务的执行状态、耗时和失败率,及时发现任务异常。
  • 避免在高负载时段执行 downsample:downsample 是一个资源密集型操作,建议在业务低峰期执行。
  • 确保分片分布合理:避免将所有分片集中在少数节点上,减少单点故障的影响。
  • 使用 ILM 时注意生命周期步骤:如果通过 ILM 自动触发 downsample,确保前面的 rollover 步骤已完成,且索引状态正常。
  • 为 downsample 任务增加重试机制:在客户端或自动化脚本中,对 missing shard 错误增加重试逻辑(但需先解决根因,不能只依赖重试)。

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

  • INFINI Console 提供集群健康、分片状态、节点指标和任务管理的可视化界面。通过 Console 可以直观地查看 downsample 任务执行状态、分片分配情况和节点健康度,快速定位是哪个分片没有返回结果。Console 还支持查看实时任务列表和集群事件,帮助判断任务失败是否与节点变更有关。

  • INFINI Gateway 可以作为 Elasticsearch 集群的流量治理网关,在 downsample 请求到达 Elasticsearch 之前进行请求检查。Gateway 可以:

    • 检测 downsample 请求的参数合法性,避免配置错误导致的分片执行失败。
    • 对 downsample 等重型操作进行限流,避免多个 downsample 任务同时执行导致节点资源耗尽。
    • 记录所有 downsample 请求和响应,便于事后分析是哪个请求触发了 missing shard 错误。
    • 在检测到 missing shard 错误时,可以根据预定义策略进行自动重试或告警。
  • 对于需要频繁执行 downsample 或管理大量索引生命周期的团队,建议结合 INFINI Console 的集群监控功能和 INFINI Gateway 的请求治理能力,建立从任务触发、执行监控、到失败恢复的完整自动化流程,减少因分片响应丢失导致的任务失败。

5. 小结 #

missing shard 是一个发生在 downsample 任务 reduce 阶段的异常,其核心含义是协调节点期望收到 N 个分片响应,但某个分片的响应为 null。这个错误的根因通常不是 reduce 逻辑本身,而是某个分片在执行阶段失败且异常没有被正确封装到响应数组中。

排查时需要从三个层面入手:分片层面(是否有分片未分配或执行失败)、节点层面(是否有节点离线或异常)、任务层面(是否有任务被取消或超时)。修复时优先确保分片和节点健康,再重新执行 downsample 任务。

对于需要长期治理的场景,建议结合 INFINI Console 和 INFINI Gateway 建立完整的可观测性和流量治理机制,将"分片响应丢失"这类问题在发生之前就通过监控和限流来预防。

相关错误 #

参考文档 #

附:日志上下文 #

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

long numIndexed = 0;
int successfulShards = 0;
for (int i = 0; i < shardsResponses.length(); i++) {
    Object shardResponse = shardsResponses.get(i);
    if (shardResponse == null) {
        throw new ElasticsearchException("missing shard");
    } else if (shardResponse instanceof DownsampleIndexerAction.ShardDownsampleResponse r) {
        successfulShards++;
        numIndexed += r.getNumIndexed();
    } else if (shardResponse instanceof Exception e) {
        throw new ElasticsearchException(e);
    }
}

这段源码来自 DownsampleIndexerActioninnerReduce 方法。它遍历每个分片的响应(shardsResponses),如果某个分片响应为 null,就抛出 missing shard 异常。正常情况下,每个分片应该返回 ShardDownsampleResponse(成功)或 Exception(失败),但绝不应该返回 null