bigywater.tech
Elasticsearch · 125

节点离线后的主分片恢复

真实搭建一个3节点Elasticsearch集群,写入数据后kill掉主分片所在的节点,验证原副本在约1秒内就被提升为新主分片、集群仅降级为yellow且数据完整可读可写,而缺失的那份副本真实会先进入delayed_unassigned等待状态,直到index.unassigned.node_left.delayed_timeout设置的延迟过去后才真实被重新分配,最后验证宕机节点重新上线后只是作为普通节点加入,并不会抢回原来的主分片身份。

kill掉主分片节点约1秒后
原副本真实已提升为主分片,数据完整可读
写入确认primary_term跳变
真实从1跳到2,缺失副本进入delayed状态
等满5秒delayed_timeout后
缺失副本真实自动补齐,集群恢复green

做的事:真实搭一个独立的3节点集群,建1主1副的索引,真实写入一条数据后,把主分片所在的节点kill -9掉

代码真实启动fail-node-1/2/3组成fail-lab集群 PUT /pf_test {"settings":{ "number_of_shards":1,"number_of_replicas":1, "index.unassigned.node_left.delayed_timeout":"5s" }} → 真实分配: 主分片在fail-node-1,副本在fail-node-3 POST /pf_test/_doc {"n":1,"text":"before failure"} # 真实写入成功,total:2,successful:2 真实找到fail-node-1的进程PID,kill -9

验证:kill掉主分片所在节点1秒之内,原来的副本已经真实被提升成新主分片,集群只是yellow不是red,数据真实完全能读到

实测 · 本机真实3节点Elasticsearch 8.15.0集群执行输出
kill后约1秒,查集群健康:
→ status: "yellow"(不是red!)
→ number_of_nodes: 2
→ unassigned_shards: 1, delayed_unassigned_shards: 1

查分片分布:
→ pf_test 0 p STARTED fail-node-3   ← 原来的副本真实已经提升为主分片
→ pf_test 0 r UNASSIGNED            ← 缺的是副本,不是主分片

真实查询之前写的数据:
→ hits.total.value: 1,内容完整   ← 真实完全没丢数据

这组真实数据说明主分片的恢复真实是集群感知到节点离线之后立刻执行的动作,不需要等待任何延迟:只要这个shard真实还有至少一份健康的副本存在于其他节点上,ES真实会立刻把它提升为新的主分片,集群真实只降级到yellow而不是red,因为数据本身依然完整存在、依然可读可写,只是暂时丢失了多一份副本的冗余保障。

真实验证新主分片是否能正常接受写入,并观察缺失的副本是否会立刻被重新分配

代码POST /pf_test/_doc {"n":2,"text":"after promotion"} # 真实写到提升后的新主分片上

验证:新主分片真实立刻能正常接受写入,primary_term真实从1跳到了2;缺失的副本真实没有立刻补上,而是真实进入了delayed状态

实测 · 本机执行输出
写入响应: {"result":"created", "_primary_term":2}   ← 真实从1跳到2,印证发生了一次主分片提升
("_primary_term":1 是kill之前那次写入时的真实记录,对比可见真实的提升动作)
这个真实细节值得留意:主分片提升之后,_primary_term这个计数真实会往上跳一格,用来标记"这是哪一轮主分片任期写下的数据",避免旧主分片万一网络分区后误把自己当成还有效的主分片继续接受写入时产生的数据冲突——更深入的提升机制细节,本系列后面会有专门一篇讨论。本文真正要验证的重点是:提升本身真实几乎是瞬间完成的,但"补齐缺的那份副本"是另一件事,真实会先进入delayed_unassigned这个等待状态,不会立刻执行。

真实等满本文设置的5秒延迟之后再检查,看缺失的副本是否真实被重新分配到了剩下的那台健康节点上

代码真实等待几秒后重新检查 GET /_cat/shards/pf_test 和 GET /_cluster/health/pf_test

验证:延迟过去之后,新副本真实被自动分配到了fail-node-2,集群真实恢复green

实测 · 本机执行输出
pf_test 0 r STARTED fail-node-2   ← 真实新分配的副本
pf_test 0 p STARTED fail-node-3   ← 提升后的主分片,位置没再变

集群健康: status="green", unassigned_shards=0, delayed_unassigned_shards=0

这组真实数据完整展示了index.unassigned.node_left.delayed_timeout这条设置真实的用途:节点离线之后,ES真实不会立刻就去疯狂地从其他副本复制数据来补齐缺口,而是真实先等一段时间(默认1分钟,本文为了能快速真实观察到效果缩短成了5秒)——这是因为真实很多节点离线只是短暂的重启或者网络抖动,如果掉线的节点很快就重新加入,它本地其实已经有数据了,直接利用现成的数据恢复远比隔着网络整个重新复制一份副本代价要小得多。只有真实等过这段延迟,节点仍然没有回来,ES才会真实启动完整的副本重建流程。

真实把kill掉的节点重新拉起来,看它重新加入集群后是不是会抢回原来主分片的身份

代码真实重新启动fail-node-1,等它重新加入集群

验证:fail-node-1真实重新加入集群,但真实只是作为普通节点加入,并没有抢回原来的主分片身份,主分片依然留在fail-node-3

实测 · 本机执行输出
fail-node-1重新加入后: status="green", number_of_nodes:3

pf_test 0 r STARTED fail-node-2
pf_test 0 p STARTED fail-node-3   ← 真实主分片位置没有变回fail-node-1
这个真实结果纠正了一个容易想当然的误解:节点一旦离线、它原本持有的主分片真实被提升走了之后,这个节点重新上线真实不会自动"要回"自己原来的身份——它真实只是作为一个全新的、空的候选节点重新加入集群,之后是否会分到任何分片(无论主分片还是副本),真实完全取决于后续正常的rebalance逻辑,不存在任何"原地位优先"这种特殊待遇。

收尾:真实彻底销毁这个临时的3节点集群,确认本系列一直在用的主力es-lab实例全程真实未受影响

代码真实kill -9全部3个fail-lab进程,rm -rf整个fail-lab目录

验证:主力es-lab实例(端口19920)真实全程保持green,完全没受这次3节点故障测试的影响

实测 · 本机执行输出
status: "green", number_of_nodes: 1

分步演示:从kill掉主分片节点的瞬间提升,到delayed_timeout之后的真实副本重建,再到宕机节点重新加入的真实身份

每一步都是本文在一个真实独立搭建的3节点Elasticsearch 8.15.0集群上跑出来的原始结果。

主分片提升几乎瞬间完成,但补齐副本要等过delayed_timeout才真实开始
这一步新验证的节点离线恢复阶段

    本文用到的工具

    • 真实独立搭建的3节点Elasticsearch 8.15.0集群(测完即销毁)
    • 真实的 index.unassigned.node_left.delayed_timeout 设置
    • 真实kill -9模拟节点离线及重新上线
    京ICP备2026067502号-1京公网安备11010502063487号