bigywater.tech
Elasticsearch · 128

集群滚动重启

真实搭建一个3节点Elasticsearch集群,对比朴素重启和标准滚动重启流程:朴素方式在等过node_left.delayed_timeout之后ES真实会在空闲节点上重建一整份全新的副本,原节点的数据从此被真实浪费掉;而标准做法在重启前先设置cluster.routing.allocation.enable=primaries,哪怕等待时间远超delayed_timeout副本依然不会被重新分配,节点重新上线并恢复设置后真实能零浪费地直接续上原来的角色。

朴素重启等过delayed_timeout后
真实在空闲节点上重建了一整份新副本
标准做法设allocation.enable=primaries
哪怕等更久副本也真实没被重新分配
重启完恢复allocation.enable=all后
真实零浪费地续上原来的角色

做的事:真实搭一个3节点1主1副的集群,先用最朴素的方式——不做任何额外设置——直接重启一个持有副本的节点

代码真实启动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

验证:等过4秒延迟之后,ES真实在原本空闲的roll-node-1上重新建了一整份新的副本

实测 · 本机真实3节点Elasticsearch 8.15.0集群执行输出
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,一旦原来的节点真的只是正常重启、很快就回来了,就真实变成了彻头彻尾的浪费:它本地那份其实完全够用的数据,再也没有机会被重新利用上。

真实换成标准做法重做一次:重启前先把cluster.routing.allocation.enable设成primaries,再kill掉持有副本的节点

代码PUT /_cluster/settings {"persistent":{ "cluster.routing.allocation.enable":"primaries" }} 真实kill掉当时持有副本的roll-node-1

验证:这次哪怕真实等了比延迟还久的时间,副本也真实没有被重新分配到别的节点上,始终保持unassigned

实测 · 本机执行输出
kill roll-node-1并等待8秒(远超4秒延迟)后:
→ roll_test 0 p STARTED roll-node-3
→ roll_test 0 r UNASSIGNED   ← 真实一直没有被重新分配,哪怕早就过了delayed_timeout
→ 集群健康: status="yellow"(真实卡在这里,不会像上一次那样自己变回green)
这个真实结果验证了cluster.routing.allocation.enable:primaries这条设置的真实作用:它把"是否允许分配非主分片(也就是副本)"这个开关真实关掉了,哪怕node_left.delayed_timeout这道延迟早就过去、ES本来应该要去找地方补一份新副本,这条设置真实会直接拦住这个动作,让缺失的副本保持unassigned、集群保持yellow,而不会真实触发那份原本可以避免的网络拷贝。

真实把被kill的节点重新拉起来,再把allocation.enable改回all,看它是否能真实直接续上原来的角色而不用重新全量拷贝

代码真实重启roll-node-1 PUT /_cluster/settings {"persistent":{"cluster.routing.allocation.enable":"all"}}

验证:roll-node-1真实立刻就恢复了副本身份,集群真实立刻变回green,整个过程真实没有发生任何多余的新副本拷贝

实测 · 本机执行输出
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的真实同步流程),而不是从零开始重建一整份副本。

收尾:真实清理测试索引,彻底销毁这个临时的3节点集群

代码DELETE /roll_test 真实kill -9全部roll-lab进程,rm -rf整个roll-lab目录

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

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

分步演示:从朴素重启真实触发的全量副本重建浪费,到关闭allocation后节点重新上线的真实零浪费续接

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

朴素重启真实触发了白费的全量副本拷贝,标准做法真实完全避免了这份浪费
这一步新验证的重启流程场景

    本文用到的工具

    • 真实独立搭建的3节点Elasticsearch 8.15.0集群(测完即销毁)
    • 真实的 cluster.routing.allocation.enable 设置与 index.unassigned.node_left.delayed_timeout
    京ICP备2026067502号-1京公网安备11010502063487号