4. 分片分配与再平衡故障诊断

分片分配是 Elasticsearch 集群把索引的每个分片落到某个数据节点上的过程,它由主节点(master)统一决策,数据节点只负责执行。当集群出现黄色或红色状态时,绝大多数情况并不是数据丢失,而是某些分片找不到合适的落点。理解“为什么没分配”和“为什么反复搬动”,是分片运维的核心能力。

一、分片未分配的常见原因

主节点在分配分片时会依次检查一系列条件,任何一条不满足都会导致分片停留在 UNASSIGNED 状态。常见原因可以归为几类:

  • 节点数量不足:副本分片要求与主分片不在同一节点,若数据节点数少于 主分片数 + 1,副本永远无法分配,集群长期黄色。
  • 磁盘水位线触发:节点磁盘使用率超过低水位线后,Elasticsearch 不再向该节点分配新分片;超过高水位线后,会主动把分片迁出。
  • 分配过滤规则:index.routing.allocation.include/exclude/require 或节点属性(如 node.attr.zone)把分片限制到了不存在的节点上。
  • 节点离线或恢复延迟:节点宕机后,主节点默认等待 index.unassigned.node_left.delayed_timeout(默认 1m)才重新分配,期间分片显示未分配。
  • 分片数超过节点承载上限:cluster.max_shards_per_node 或 index.routing.allocation.total_shards_per_node 限制被突破。
  • 主分片缺失:主分片所在节点数据损坏且无副本,需要 allocate_stale_primary 或 reroute 手动干预。

诊断的第一步永远是查看未分配原因,而不是盲目重试:

GET _cluster/allocation/explain
{
  "index": "logs-2024.06",
  "shard": 0,
  "primary": false
}

返回结果中的 unassigned_info.reason 会直接给出 NODE_LEFT、ALLOCATION_FAILED、CLUSTER_RECOVERED 等根因,explanation 字段则逐节点说明“为什么这个节点也不行”,这是定位问题最有效的入口。

二、分配决策日志解读

主节点日志中与分配相关的关键行通常包含 ClusterApplierService、AllocationService 和 GatewayAllocator 等类名。典型日志如下:

[AllocationService] failed to allocate [logs-2024.06][2] on node [node-3],
reason [NO(disk watermark [90%] exceeded on node-3)]

解读要点:方括号中的分片标识说明是哪个索引的哪个分片;on node 指出候选节点;reason 给出拒绝原因。若日志反复出现 shard has exceeded the maximum number of retries,说明分配尝试次数已达上限,需要手动 POST _cluster/reroute?retry_failed=true 重置重试计数。

三、再平衡阈值调优

再平衡由三个阈值控制,它们决定“节点间分片数量差多少才触发搬动”:

参数默认值作用
cluster.routing.allocation.balance.shard0.45分片数量均衡权重
cluster.routing.allocation.balance.index0.55每索引分片均衡权重
cluster.routing.allocation.balance.threshold1.0触发再平衡的最小权重差

当集群节点数多、分片数少时,默认阈值可能导致频繁小幅搬动,增加恢复压力。此时可适当调大 threshold(如 1.5~2.0),让集群容忍更大的不均衡,减少无谓迁移。反之,若希望更均匀,可调小该值,但要接受更高的网络与 IO 开销。调优后建议用 GET _cat/allocation?v 观察各节点 shards 列的差值是否收敛到可接受范围。

四、磁盘水位线触发场景与恢复

磁盘水位线是生产环境最常见的分配阻塞源。默认三档:低水位线 85%、高水位线 90%、洪水水位线 95%。触发逻辑如下:

磁盘水位线触发与分片分配状态 正常分配区 85% 90% 95% 低水位线 85% 高水位线 90% 洪水水位线 95% 低于 85% 正常接收新分片 可参与再平衡 分配决策无限制 85% ~ 90% 不再分配新分片 已有分片保留 集群可能变黄 高于 90% 主动迁出分片 95% 时索引只读 写入被拒绝 清理磁盘 / 扩容节点 / 调整水位线后,分片重新分配

恢复操作按优先级排列:

  1. 清理磁盘:删除过期索引、执行 POST /index/_forcemerge?only_expunge_deletes=true 回收被删除文档空间,或迁移冷数据到对象存储。
  2. 临时放宽水位线:在紧急情况下可动态调整,例如 PUT _cluster/settings {"transient":{"cluster.routing.allocation.disk.watermark.low":"90%","cluster.routing.allocation.disk.watermark.high":"95%"}},但必须同步扩容,否则只是推迟问题。
  3. 扩容数据节点:新增节点后,主节点会自动把分片迁往新节点,观察 _cat/recovery 确认迁移完成。
  4. 手动重路由:当自动分配失败时,用 POST _cluster/reroute 指定 move 或 allocate_replica,并配合 retry_failed=true 清除失败计数。
水位线判断依据是节点数据路径所在文件系统的使用率,而非整个磁盘。多路径配置时以最紧张的那个路径为准,排查时可用 GET _cat/allocation?v 查看 disk.percent 列。
洪水水位线(95%)触发后,所有写入该节点的索引会变为只读(index.blocks.read_only_allow_delete)。即使磁盘清理后,该块也不会自动解除,需要手动执行 PUT /index/_settings {"index.blocks.read_only_allow_delete": null}。

综合来看,分片分配故障的诊断路径是:先用 _cluster/allocation/explain 拿到根因,再对照水位线、过滤规则、节点数量三类条件逐一排除,最后通过清理、扩容或重路由恢复。再平衡阈值调优属于预防性手段,应在集群稳定期小步调整并持续观察,而不是在故障中临时大改。