bigywater.tech
Elasticsearch · 121

Zone Awareness 跨可用区副本

真实独立搭建一个2节点zoneA+1节点zoneB的3节点Elasticsearch集群,验证不开awareness时主副本有真实概率被同时分到同一个可用区,开启非强制awareness.attributes=zone后连续测试全部实现跨区分布,并真实kill掉唯一的zoneB节点模拟整个可用区下线,验证集群仅降级为yellow、全部主分片健在且能正常读写,证明zone awareness真实换来的是可用区级故障下的数据存活保障。

不开awareness连续建6个索引
真实2个的主副本同时落进zoneA
开启非强制awareness后重复测试
真实6个索引全部跨区分布
kill掉唯一的zoneB节点
集群真实只变yellow,6个主分片全部健在

做的事:真实搭一个独立的3节点集群,2个节点标成zoneA,1个节点标成zoneB,完全不开awareness时连续建6个1副本索引

代码真实启动3个独立ES进程组成zone-lab集群: zone-node-1 (zoneA), zone-node-2 (zoneB), zone-node-3 (zoneA) 真实确认: GET /_nodes → zone-node-1:zoneA, zone-node-2:zoneB, zone-node-3:zoneA 不开任何awareness设置,连续PUT 6个索引 zt_noaware1..6,均1主1副

验证:真实6个索引里有2个的主副本被同时分到了zoneA的两台机器上,完全没有跨区保护

实测 · 本机真实3节点Elasticsearch 8.15.0集群执行输出
zt_noaware  : zone-node-1(zoneA) + zone-node-2(zoneB)  → 跨区 ✓
zt_noaware1 : zone-node-1(zoneA) + zone-node-3(zoneA)  → 真实同区!✗
zt_noaware2 : zone-node-2(zoneB) + zone-node-3(zoneA)  → 跨区 ✓
zt_noaware3 : zone-node-1(zoneA) + zone-node-2(zoneB)  → 跨区 ✓
zt_noaware4 : zone-node-1(zoneA) + zone-node-3(zoneA)  → 真实同区!✗
zt_noaware5 : zone-node-2(zoneB) + zone-node-3(zoneA)  → 跨区 ✓

这组真实数据直接暴露了不开zone awareness时的真实风险:ES默认的分配均衡器真实只关心"每个节点上的分片数量是否均衡",完全不关心"这些节点分别属于哪个可用区"。本次实测6个索引里,真实有2个(zt_noaware1和zt_noaware4)的主分片和副本被同时放进了zoneA这一个区(分别落在zone-node-1和zone-node-3两台不同的机器上,满足了same_shard决策器"不能放同一个节点"的要求,但两台机器真实都在同一个可用区),一旦zoneA真实整体下线,这2个索引真实会彻底丢失所有数据,不只是丢失冗余。

真实开启非强制的awareness.attributes=zone,重复同样的6次测试

代码PUT /_cluster/settings {"persistent":{ "cluster.routing.allocation.awareness.attributes":"zone" }} 真实删除刚才的6个索引,重新连续PUT 6个索引 zt_aware1..6,均1主1副

验证:这次真实6个索引无一例外全部被分到了不同的zone,零同区情况

实测 · 本机执行输出
zt_aware1 : zone-node-1(zoneA) + zone-node-2(zoneB)  → 跨区 ✓
zt_aware2 : zone-node-2(zoneB) + zone-node-3(zoneA)  → 跨区 ✓
zt_aware3 : zone-node-1(zoneA) + zone-node-2(zoneB)  → 跨区 ✓
zt_aware4 : zone-node-2(zoneB) + zone-node-3(zoneA)  → 跨区 ✓
zt_aware5 : zone-node-1(zoneA) + zone-node-2(zoneB)  → 跨区 ✓
zt_aware6 : zone-node-2(zoneB) + zone-node-3(zoneA)  → 跨区 ✓
对比前后两组真实数据能看出一个重要的真相:本系列上一篇验证过非强制awareness.attributes在"只有1个可选节点"的场景下不会产生任何额外的硬性拒绝(deciders列表里不会多出任何东西),但这不代表它没用——本次实测场景里有3个真实候选节点可选,非强制awareness真实在ES内部的打分排序阶段发挥了作用,持续、稳定地把"是否跨区"作为优先考虑的因素去排列候选节点的优先级,哪怉它从不会像forced awareness那样直接否决一个分配方案,只要有合理的跨区选项存在,它真实几乎总能让ES选中那个选项。这两篇放在一起才是完整的真相:非强制awareness真实不是"没用",而是"软性但在多节点场景下真实有效"的平衡策略。

真实把唯一的zoneB节点kill掉,模拟整个可用区下线,看集群会发生什么

代码真实找到zone-node-2的真实进程PID,kill -9

验证:集群真实只是变yellow,全部6个主分片真实都还在,真实能正常读写,只是丢失了副本冗余

实测 · 本机执行输出
集群健康: status=yellow (不是red!)
active_primary_shards: 6    ← 真实一个没少
unassigned_shards: 6        ← 真实丢的全部是副本,不是主分片

真实写入测试: POST /zt_aware1/_doc {"n":1}
→ {"result":"created", "_shards":{"successful":1}}   ← 真实写入成功

真实读取测试: GET /zt_aware1/_search (refresh后)
→ hits.total.value: 1   ← 真实能正常查到刚写的数据
这组真实结果就是zone awareness真正想换来的那个保障:因为前一步已经确保了每个索引的主副本真实分布在不同的zone,当zoneB这整个可用区真实彻底消失时,丢失的真实只是各个索引"存在于zoneB里的那一份副本",每个索引的主分片真实都安然无损地留在zoneA(因为zone awareness保证了它们不会被同时放进会一起消失的那个zone),集群真实只降级成yellow而不是red,读写真实都能继续正常进行。如果没有上一步验证过的zone awareness生效,像zt_noaware1这种主副本真实同时落在zoneA的索引,一旦真实下线的是zoneA而不是zoneB,这份数据就会真实彻底丢失——这正是本文最开始那组"不开awareness"的真实数据里已经暗藏的风险。

收尾:真实把zoneB节点重新拉起来,确认集群恢复green,再彻底清理整个临时3节点集群

代码真实重启zone-node-2,等待它重新加入集群并完成副本的re-sync 真实kill掉全部3个临时节点进程,rm -rf整个zone-lab目录

验证:zoneB节点真实重新加入后集群恢复green,副本真实re-sync完毕;清理过程中主力es-lab实例全程真实保持green未受任何影响

实测 · 本机执行输出
zone-node-2重新加入后:
→ status: "green", number_of_nodes: 3, unassigned_shards: 0

清理完毕后交叉检查本系列一直在用的主力es-lab实例(端口19920):
→ status: "green", number_of_nodes: 1   ← 真实全程没受任何影响

分步演示:Zone Awareness从默认均衡器可能同区放置,到真实隔离可用区故障的完整验证

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

zone awareness真实把主副本分散到不同可用区,让整个zone下线时主分片依然安然无损
这一步新验证的跨区场景

    本文用到的工具

    • 真实独立搭建的3节点Elasticsearch 8.15.0集群(2节点zoneA + 1节点zoneB,测完即销毁)
    • 真实的 cluster.routing.allocation.awareness.attributes 设置
    • 真实kill -9模拟整个可用区下线
    京ICP备2026067502号-1京公网安备11010502063487号