《深入理解 Nacos 集群与 Raft 协议》系列五:为什么集群未过半,系统就不可用?从 Raft 的投票机制说起

《深入理解 Nacos 集群与 Raft 协议》系列

大家好,我是G探险者!

很多人部署 Nacos 集群时,会遇到这样一个问题:

我明明部署了 5 个节点,但只启动了 2 个,为什么系统完全无法使用?

这不是 Nacos 的 Bug,而是 Raft 共识算法的一个核心设计 ------ "过半存活原则"

本篇我们将深入讲解:

  • 为什么需要超过一半节点存活?
  • 如果未过半,系统会发生什么?
  • 如何从理论上理解这个约束?

一、Raft 的"过半投票原则"

Raft 在以下两个地方都要求"过半节点确认"才能推进:

  1. 选主(Leader Election):候选人需要获得超过半数节点投票才能成为 Leader
  2. 日志提交(Log Commitment):Leader 的日志条目,必须被超过半数节点确认,才能算"已提交"

这两个约束,是整个 Raft 一致性协议的核心。

→ 所以,没有过半节点存活,就选不出 Leader,无法提交日志,系统即不可用。


二、举例说明:三种典型节点数量

节点数 最小存活数(过半)
3 2
5 3
8 5

假设你部署了 8 个节点,只启动了 4 个,那就无法满足"超过 8/2 = 4"这个条件 → 系统无法进行任何写操作,甚至部分读操作也会失败(因为没有 Leader)。


三、为什么非得"过半"?

这是为了防止脑裂和数据丢失

我们来思考一个问题:

如果 5 个节点中,A、B 两个节点单独启动,不满足过半,但系统仍允许它们工作,那会发生什么?

  • A 成为了 Leader,写入一条日志(仅 A、B 有)
  • 突然 A、B 宕机,C、D、E 启动,C 成为新 Leader
  • C 重复写入了另一条日志(无 A、B 的写入)

→ 日志分叉!数据丢失!

为了避免这种情况,Raft 要求任何 Leader 的日志必须获得"过半副本确认",才能提交。

而一个节点想当选为 Leader,也必须能拿到过半选票。

所以,如果你活跃节点不满过半:

  • 不能选主
  • 原 Leader 无法继续领导(因心跳无响应)
  • 所有提交机制暂停

系统进入保护性停顿状态,而非错误工作。


四、Nacos 集群部署的建议

✅ 正确部署姿势

  • 总节点数建议奇数(3、5、7、9),避免投票平票
  • 启动时应确保至少过半节点在线,否则系统不工作

⚠️ 错误部署陷阱

  • 部署 8 个节点,仅启动 4 个 → 失败
  • 以为"部分可用" → 实则"不一致风险" → Raft 拒绝启动

五、可用性 vs 一致性

Raft 在设计时就做出了选择:

牺牲"部分可用性",换取"数据强一致性"

也就是说:

  • 哪怕你能读取,但 Leader 不确定日志已同步 → 暂停处理
  • 系统保证数据一致,而不是"不管三七二十一先返回"

这也是 Nacos、etcd、Consul 等注册中心使用 Raft 的原因:

  • 配置信息必须强一致(否则灰度配置不生效)
  • 服务列表变更必须可靠(否则服务发现错乱)

六、如何判断当前集群是否满足"过半"?

查看 Nacos 控制台日志或访问 /nacos/v1/ns/operator/raft/cluster 接口:

json 复制代码
{
  "leader": "nacos-1",
  "raftPeers": [
    {"ip": "nacos-1", "state": "LEADER"},
    {"ip": "nacos-2", "state": "FOLLOWER"},
    {"ip": "nacos-3", "state": "FOLLOWER"},
    ...
  ]
}

如日志中出现:

text 复制代码
no leader is available now!

说明未能选主,系统已进入"挂起"状态。


七、总结

Raft 是为了保证数据一致性而牺牲了可用性的一种协议。

  • 所有操作都要过半节点参与确认
  • 未过半时,为防止脑裂,系统主动进入保护性不可用
  • 在 Nacos 中体现为:Leader 无法选举、写请求挂起、读请求可能失败
相关推荐
coderSong256842 分钟前
Java高级 |【实验八】springboot 使用Websocket
java·spring boot·后端·websocket
Mr_Air_Boy2 小时前
SpringBoot使用dynamic配置多数据源时使用@Transactional事务在非primary的数据源上遇到的问题
java·spring boot·后端
咖啡啡不加糖3 小时前
Redis大key产生、排查与优化实践
java·数据库·redis·后端·缓存
大鸡腿同学3 小时前
纳瓦尔宝典
后端
棠十一3 小时前
Rabbitmq
分布式·docker·rabbitmq
Lansonli4 小时前
大数据Spark(六十一):Spark基于Standalone提交任务流程
大数据·分布式·spark
2302_809798324 小时前
【JavaWeb】Docker项目部署
java·运维·后端·青少年编程·docker·容器
zhojiew5 小时前
关于akka官方quickstart示例程序(scala)的记录
后端·scala
sclibingqing5 小时前
SpringBoot项目接口集中测试方法及实现
java·spring boot·后端