什么是 Raft 算法
Raft 是一种分布式一致性算法,专门用于管理复制日志系统。它由 Diego Ongaro 和 John Ousterhout 在2014年提出,目的是提供一种比传统 Paxos 算法更易于理解和实现的替代方案。
与 Paxos 的对比
Raft 提供与 Paxos 相同的功能和性能保证,但在算法结构和实现方式上有显著不同:
- 更加模块化:将复杂问题分解为多个独立子问题
- 更直观:通过明确的角色划分和状态转换简化理解
- 更适合工程实现:提供了完整的算法细节,而非理论框架
Raft 的核心模块
Raft 将一致性算法分解为三个关键模块:
1. 领导人选举(Leader Election)
- 集群中任何时候最多只能有一个有效的领导者
- 节点分为三种角色:领导者(Leader)、跟随者(Follower)和候选人(Candidate)
- 选举过程使用随机超时机制来避免投票分裂
2. 日志复制(Log Replication)
- 领导者接收客户端请求并写入日志
- 领导者将日志条目复制到所有跟随者节点
- 当多数节点确认接收日志后,该日志被视为已提交
- 已提交的日志最终会被所有节点应用
3. 安全性(Safety)
- 选举限制:只有包含所有已提交日志的节点才能成为领导者
- 日志匹配特性:如果两个日志在相同索引位置有相同任期,那么它们完全相同
- 状态机安全特性:一旦日志条目在某台服务器上被应用,其他服务器不能在相同索引位置应用不同的命令
Raft 算法的两阶段运行
第一阶段:选举过程
- 初始所有节点都是跟随者状态
- 跟随者在选举超时(通常150-300ms)后未收到领导者心跳,转变为候选人
- 候选人发起选举,向其他节点请求投票
- 获得多数票的候选人成为新领导者
- 新领导者开始向跟随者发送心跳维持权威
第二阶段:正常操作
- 领导者处理所有客户端请求
- 每个客户端请求首先被记录为日志条目
- 领导者并行地将日志复制到跟随者节点
- 当日志被复制到多数节点后,领导者应用该日志并通知跟随者应用
- 领导者定期发送心跳(通常每50ms)来维持领导地位
实际应用场景
Raft 算法广泛应用于各种分布式系统,包括:
- 分布式键值存储(如etcd、Consul)
- 分布式数据库(如CockroachDB、TiDB)
- 容器编排系统(如Kubernetes)
- 区块链共识机制
领导人Leader选举
Raft 通过选举一个领导人,然后给予他全部的管理复制日志的责任来实现一致性。
在Raft中,任何时候一个服务器都可以扮演下面的角色之一:
- 领导者Leader:处理客户端交互,日志复制等动作,一般一次只有一个领导者
- 候选者Condidate:候选者就是选举过程中提名自己的实体,一旦选举成功,则成为领导者
- 跟随者Follower:类似选民,完全被动的角色,这样的服务器等待被通知投票
而影响他们身份变化的则是选举。
Raft 使用心跳机制来触发选举,当Server启动的时候,初始状态都是 Follower。每一个Server都有一个定时器,超时时间为 Election timeout (一般为150-300ms),如果某Server没有超时的情况下收到自领导者或者候选者的任何消息,定时器重启,如果超时,它就开始一次选举。
节点异常类型及处理机制
Leader 不可用
当集群中的Leader节点发生故障时,会导致整个集群暂时无法处理写请求。这种情况可能由以下原因引起:
- 服务器硬件故障(如CPU过载、内存耗尽)
- 网络分区导致Leader与其他节点失去连接
- Leader进程崩溃或被强制终止
典型处理流程:
- Follower节点检测到与Leader的心跳超时(通常设置150-300ms的超时阈值)
- Follower转为Candidate状态并开始新一轮选举
- 新Leader选举成功后接管集群管理权
当一段时间之后,如果之前的Leader再次加入集群,则两个Leader比较彼此的步进数,步进数低的Leader将切换自己的状态为 Follower。较早前 Leader 中不一致的日志将被清除,并与现有的Leader中的日志保持一致。
Follower 不可用
当部分Follower节点失效时,集群仍能保持基本运作,但会影响数据复制的一致性。常见场景包括:
- 短暂网络抖动导致Follower暂时失联
- Follower节点磁盘空间不足无法写入日志
- 配置错误导致Follower无法加入集群
恢复策略:
- Leader持续尝试与Follower建立连接
- 当Follower恢复后,通过日志复制追赶最新状态
- 若长期无法恢复,可能触发自动节点替换机制
Follower 节点不可用的情况相对容易解决,因为集群中的日志内容始终是从Leader节点同步的,只要这一节点再次加入集群时重新从Leader节点处复制日志即可。
选举冲突(多个Candidate/Leader)
在特定网络条件下可能出现选举异常:
- 网络分区导致不同分区各自选举出Leader
- 选举超时设置不合理导致频繁选举
- 节点时钟不同步影响选举逻辑
解决方案:
- 采用PreVote机制防止频繁选举
- 设置合理的选举超时参数(建议Follower超时为Leader的2-3倍)
- 实现任期(Term)机制确保只有一个合法Leader
在集群中出现多个 Candidate 或 多个Leader通常是由于数据传输不畅造成的,出现多个Leader的情况相对少见,但多个 Candidate 比较容易出现在集群节点启动初期尚未选出Leader的混沌时期。
Candidate 继续向其他的 Follower 询问。由于一些 Follower 已经投过票了,所以均返回拒绝接受。在步进数相同的情况下,Candidate 将拒绝接受另一个 Candidate 的请求。由于第一次未选出 Leader,Candidate 将随机选择一个等待间隔(150ms-300ms)再次发起投票。
如果得到集群中的半数以上的Follower的接受,这一Candidate将成为Leader。在被多数节点拒绝之后,并已知集群中存在 Leader后,这一Candidate 节点被终止投票请求、切换为Follower,从Leader节点同步日志。
新节点加入集群
集群扩容时新节点加入需要特殊处理:
1. 配置阶段:
- 准备新节点硬件资源
- 安装相同的服务软件版本
- 配置初始集群信息
2. 加入流程:
- 新节点以Learner模式启动
- 从Leader同步完整状态数据
- 达到同步阈值后转为正式Follower
3. 数据同步策略:
- 快照传输(适用于大状态)
- 增量日志复制
- 一致性检查机制