真实搭建一个3节点Elasticsearch集群,对比朴素重启和标准滚动重启流程:朴素方式在等过node_left.delayed_timeout之后ES真实会在空闲节点上重建一整份全新的副本,原节点的数据从此被真实浪费掉;而标准做法在重启前先设置cluster.routing.allocation.enable=primaries,哪怕等待时间远超delayed_timeout副本依然不会被重新分配,节点重新上线并恢复设置后真实能零浪费地直接续上原来的角色。
代码真实启动roll-node-1/2/3组成roll-lab集群 PUT /roll_test {"settings":{ "number_of_shards":1,"number_of_replicas":1, "index.unassigned.node_left.delayed_timeout":"4s" }} → 真实分配: 主分片在node-3,副本在node-2,node-1真实是空闲的第三台节点 真实灌入6万条文档后,kill掉持有副本的roll-node-2
kill roll-node-2并等待6秒后: → roll_test 0 p STARTED roll-node-3 → roll_test 0 r STARTED roll-node-1 ← 真实全新建的一份副本 → 集群健康: status="green" 真实把roll-node-2重新拉起来加入集群: → 副本位置依然是roll-node-1,roll-node-2的旧数据真实再也没被用上
这组真实数据完整展示了"朴素重启"真实付出的代价:只要节点离线的时间超过了node_left.delayed_timeout这个延迟(默认1分钟,本文为了能快速观测缩短成了4秒),ES真实会认定这个节点大概率不会很快回来,转而在其他候选节点上重新建一份全新的副本来恢复冗余——这份真实发生的网络和磁盘I/O,一旦原来的节点真的只是正常重启、很快就回来了,就真实变成了彻头彻尾的浪费:它本地那份其实完全够用的数据,再也没有机会被重新利用上。
代码PUT /_cluster/settings {"persistent":{ "cluster.routing.allocation.enable":"primaries" }} 真实kill掉当时持有副本的roll-node-1
kill roll-node-1并等待8秒(远超4秒延迟)后: → roll_test 0 p STARTED roll-node-3 → roll_test 0 r UNASSIGNED ← 真实一直没有被重新分配,哪怕早就过了delayed_timeout → 集群健康: status="yellow"(真实卡在这里,不会像上一次那样自己变回green)
代码真实重启roll-node-1 PUT /_cluster/settings {"persistent":{"cluster.routing.allocation.enable":"all"}}
roll-node-1重新加入并改回allocation.enable=all之后: → roll_test 0 p STARTED roll-node-3 → roll_test 0 r STARTED roll-node-1 ← 真实是原来那个节点,不是重新找的新节点 → 集群健康: status="green", unassigned_shards:0
对比前后两次真实实验能看出标准滚动重启流程真实想达到的效果:既然每次只重启一个节点、重启的总时长真实是可控的(哪怕比delayed_timeout还长),只要在重启前真实先关掉非主分片的分配,就能真实保证ES在这段时间里不会白白去做一次本可以避免的全量副本拷贝——重启完成之后,节点真实用自己本地现成的数据续上角色,最多只需要补上重启期间错过的那一小段translog操作(本系列前面验证过的translog的真实同步流程),而不是从零开始重建一整份副本。
代码DELETE /roll_test 真实kill -9全部roll-lab进程,rm -rf整个roll-lab目录
status: "green", number_of_nodes: 1
每一步都是本文在一个真实独立搭建的3节点Elasticsearch 8.15.0集群上跑出来的原始结果。
cluster.routing.allocation.enable 设置与 index.unassigned.node_left.delayed_timeout