适用版本: 7.12-8.9
1. 错误异常的基本描述 #
missing shard 是 Elasticsearch 在执行 downsample(下采样) 任务时抛出的异常,表示协调节点在汇总各分片返回结果时,发现 shardsResponses 数组中某个位置本应有分片响应,实际却拿到了 null。这个错误不是普通搜索里的"找不到分片",而是后台任务在遍历分片级执行结果时,发现某个分片根本没有返回有效响应。
从源码来看,该异常发生在 DownsampleIndexerAction 的 innerReduce 阶段:
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 任务被手动取消(通过
_tasksAPI)或超时取消,某些分片的响应槽位可能未被正确填充。 - 并发竞态条件:在极端情况下,内部 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 数组中的每个元素必须是以下三种情况之一:
- 一个有效的
ShardDownsampleResponse对象 - 一个
Exception对象 - 不能是
null(否则抛missing shard)
3. 如何排查和解决这个异常 #
建议按"先确认任务状态,再定位失败分片,最后判断是节点问题还是数据问题"的顺序处理:
- 先确认 downsample 任务的完整错误信息,包括任务 ID、源索引、目标索引、失败时间点和异常堆栈。
- 查看任务执行期间各分片的执行状态,确认是否有分片失败、节点离线或任务被取消。
- 检查源索引和目标索引的分片分配状态、节点健康度和集群状态,确认没有未分配或异常的分片。
- 查看 Elasticsearch 日志中 downsample 相关的
WARN和ERROR级别日志,重点关注DownsampleIndexerAction、TransportDownsampleAction等类。 - 如果是偶发问题,检查任务执行期间是否有节点重启、分片迁移、集群状态变更或网络抖动。
相关 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"
重点关注:
- 任务状态是否为
FAILED或CANCELLED。 - 是否有
cancellation或node_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状态。 - 是否存在
UNASSIGNED、INITIALIZING、RELOCATING状态的分片。 - 分片是否分布在多个节点上,是否有节点负载不均。
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是否为green或yellow。unassigned_shards是否大于0。relocating_shards、initializing_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_interval或dimensions配置有问题,可能导致某些分片执行失败,但异常信息丢失。 - 区分数据节点和协调节点:
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 建立完整的可观测性和流量治理机制,将"分片响应丢失"这类问题在发生之前就通过监控和限流来预防。
相关错误 #
- actual-shard-is-not-a-primary:实际分片不是主分片
- all-shards-failed:所有分片均失败
- failed-to-merge-shard-results:合并分片结果失败
- no-shard-available-for:无可用分片
- primary-shard-is-not-active:主分片未激活
- index-shard-was-not-set:索引分片未设置
参考文档 #
- Elasticsearch Downsample API 官方文档
- Elasticsearch Shard Allocation 官方文档
- Elasticsearch Tasks Management 官方文档
- 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);
}
}
这段源码来自 DownsampleIndexerAction 的 innerReduce 方法。它遍历每个分片的响应(shardsResponses),如果某个分片响应为 null,就抛出 missing shard 异常。正常情况下,每个分片应该返回 ShardDownsampleResponse(成功)或 Exception(失败),但绝不应该返回 null。





