一文了解 Raft

分布式系统会有自己的独立计算和存储,节点之间的一致性富有挑战,分布式一致性需要解决两个问题:

  • 数据不能存在单个节点,容易出现单点故障
  • 多节点之间需要保证具备相同的数据

Raft 简介

Raft 在2014年提出,用来解决分布式系统中的一致性问题,是为了探索一种更易于理解的一致性算法而产生的。Raft 使用日志记录所有的操作,所有节点都有自己的日志列表记录所有的请求。节点包含三种状态:

  • leader: 接受客户端请求,并向 follower 同步请求日志,当日志同步到大多数节点上后告诉Follower提交日志
  • follower: 接受并持久化Leader同步的日志,在 leader 告知可以提交之后,提交日志。同时也可以变为candidate 参与竞选
  • candidate: leader选举过程中的临时角色

正常情况下同一时间只存在一个leader,其他节点为follower,所有client都与leader进行交互。所有操作都采用类似两阶段提交的模式,leader 在收到请求后不会立即执行,会先写到日志列表中并同步到 follower,有多数 follower都写入 leader 才会提交该操作并返回客户端结果,同时告知其他所有节点提交该操作。

每个领导者都有任期。类似于美国第X届总统一样,基于时间跨度上可以拆分为多个任期(term)。

leader 选举

leader选举过程大概包含以下过程:

  • 初始化时,节点会变为 follower 状态,一段时间内没有收到 leader 节点心跳就会切换为 candidate 去竞争 leader
  • 变为 candidate 之后,会将自身任期(term)+1,然后给自己投票,并向其他节点广播。最终结果:
    • 拿到多数选票,成为 leader
    • 发现其他节点已经是 leader,切换为 follower
    • 选举超时后重来一遍选举流程

可以结合动图进一步加深一下理解。

首次选举时,C节点变为 candidate,并向A、B广播,最终收到投票成为 leader。如果没有收到足够的选票,会再次选举直到选择出一个 leader。

当 leader C 下线时,会导致再次选举。此时A、B在指定时间内收不到 C的心跳,B先到达时间,随后 更新 term 参与竞选,A 收到消息后参与投票,C不会有回应,但是因为已经有 A 和 B 两票,A 成为新一任 leader

假设集群网络出现问题,导致节点出现了2个分区,分区1有 leader,分区2没有 leader. 此时分区2 会重新进行选举,但是由于多数选票才能成功,因此节点较少的分区无法竞选成功。即使 leader在节点较少的分区,并于client进行交互,但是由于写入的日志无法得到多数提交,因此也不能正常工作。

怎么保证最终会选举出 leader, 避免每次选举时都会有多个 candidate 同时参与竞选导致拿不到足够的选票?

candidate 每次选举都会设置一个随机超时时间

怎么避免单个节点故障,一直更新 term, 导致需要频繁的竞选 leader?

增加了预投票机制,candidate 分为两个阶段:预投票、正式投票

  • 预投票:不增加自身 term, 但是会广播投票请求,只有拿到多数派投票后才正式进入投票阶段
  • 正式投票:更新 term,并广播投票请求 这样,当某个节点出现了网络故障时,会切换为candidate状态,但是由于获取不到足够选票,因此不会更新 term, 后续网络恢复时也不会对现在的 leader 产生影响

前面提到过,leader 包含了任期内所有日志,并向其他 follower 复制日志。那么怎么保证 leader 保留有全部的日志?

答:candidate 发出的投票请求会带上候选者自身的 term、memberId、最新日志的 term 和 index,其他节点收到请求后会校验候选者的 term 以及日志是否都比自身的更新,如果是则同意选举。

假设当前有5台机器,多数派机器拥有最新提交的日志,又因为 leader 选举本身需要多数派选举,因此被选举出来的 leader 一定具备日志 >= 最新提交日志,所以新的 leader 节点一定包含最新提交的日志

raft 日志

leader 节点负责与客户端进行交互,当有数据提交时,整体的工作流程如下图: leader 在发送写请求过程中,会向其他节点发送日志复制请求协议,协议中包含内容:

  • leader 任期 term
  • memberId
  • 本次待复制的日志列表
  • 上一条日志的 preLogIndex 和 preLogTerm
  • 已达到多数派一致而提交的最大日志索引 commitIndex

其他节点发现 leader 任期>=自身任期 & 日志一致性检查通过(自身日志列表中包含请求中指定的preLogTerm和preLogIndex的日志),则用户请求中待复制的日志列表直接覆盖本地日志,并更新本地的 commitIndex。

以下图为例,term7 的 leader 节点是 d,产生两条日志后发生异常。一个新 leader 竞选成功,并产生了一条新日志 logTerm=8, logTermIndex=11,preLogIndex=10, preLogTerm=6. 随后的复制过程如下:

  • c、d 包含 preLogIndex=10, preLogTerm=6 的日志,直接把本次logIndex=11日志覆盖到本地,本地index=11、12的会被覆盖丢弃,随后c、d就会和 leader 保持一致
  • a 没有找到 preLogIndex 的日志,会返回拒绝复制。leader 收到拒绝复制后,进一步向前移动待复制日志列表,范围包含index=10,11,并再次发送日志复制请求。a收到之后,会直接将10,11直接复制到本地,从而和 leader 保持一致
  • 其他节点流程与a类似

通过该图也能看出:所有节点都会拥有一致的状态机输入序列,各个节点可以通过一致的初始状态+一致的状态机输入序列 从而得到一致的最终状态。Raft 会有以下的日志保证:

  1. leader 只会追加日志,不会覆盖或删除已有日志
  2. 日志的唯一性:如果两个节点的日志的 index 和 term 都相同,则这两个日志相同
  3. 日志前序兼容:如果两个节点日志相同,那么他们之前的日志均相同

日志成功复制给多数节点之后,就可以提交,但只能提交当前term下的日志,避免日志之间被覆盖。如果没有这个限制,可能出现下图情况:

  • 初始状态 a,之后 S1 下线
  • b 中 S5 从 S3 和 S4 处获得了投票成为了 Leader 并收到了一条来自客户端的消息,之后 S5 下线
  • c 中 S1 恢复并成为了 Leader,并且将日志复制给了多数结点,之后进行了一个致命操作,将 index 为 2 的日志提交了,然后 S1 下线
  • d 中 S5 恢复,并从 S2、S3、S4 处获得了足够投票,然后将已提交的 index 为 2 的日志覆盖

限制了只能提交当前 term 下日志情况下,c 的操作可能有两种:

  • S1 包含1,2,4三条日志,并成功复制到了 S1、S2、S3,随后下线。后续 S5 没有更新的 term 日志, 无法成为新 leader,因此不会覆盖 2、4 的日志
  • S1 有1、2、4 三个日志,S1在将数据复制到多数节点之前下线,此时2、4并不会被 commit,因此 S5 成为 leader 后将 2、4 覆盖,这个是符合预期的

raft 成员变更

假设我们对集群的配置进行变更,可能导致选出两个 leader.

如下图,server3 是 leader,在途中方框时刻发生宕机,server1 和 server5 同时参与选举:

  • server1: 配置仍是 old, 只需要 server1 和 server2 就能组成集群,因此可以成为 leader
  • server5: 配置为 new,只需 server3, server4, server5 就可以组成集群,可以被选举为 leader

解决方案:联合共识、单成员变更

联合共识策略下,集群分为三个阶段:

  • 集群还在使用 old 配置,此时 old 配置中的多数派达成一致即可
  • 集群写入 old_new 配置后,进入联合共识状态,此时需要 old 配置中的多数派与 new 配置中的多数派同时达成一致
  • 集群写入 new 配置,进入最终状态,此时 new 配置中多数派达成一致就可以做出决议

这个图没看明白,为什么new做决议是在new entry提交之前?

单成员变更:集群每次只变更一个节点

参考文档

相关推荐
TiDB_PingCAP4 小时前
海量数据融合互通丨TiDB 在安徽省住房公积金监管服务平台的应用实践
分布式·tidb·htap
程序员的世界你不懂5 小时前
Kafka 推送消息,移动端自动化测试,数据驱动测试
分布式·kafka·linq
Demons_kirit9 小时前
Dubbo+Zookeeper
分布式·zookeeper·dubbo
码农liuxin11 小时前
Dubbo 与 Eureka 深度对比:服务通信与发现的核心差异与选型指南
分布式·后端·dubbo
好记性+烂笔头12 小时前
Hadoop八股
大数据·hadoop·分布式
Python数据分析与机器学习12 小时前
《基于Hadoop的出租车需求预测系统设计与实现》开题报告
大数据·hadoop·分布式·python·算法·数据挖掘·数据分析
StableAndCalm13 小时前
什么是hadoop
大数据·hadoop·分布式
麻芝汤圆13 小时前
在虚拟机上安装 Hadoop 全攻略
大数据·linux·服务器·hadoop·windows·分布式
lqlj223313 小时前
第一个Hadoop程序
大数据·hadoop·分布式
计算机软件程序设计16 小时前
Windows下安装kafka
windows·分布式·kafka