Raft算法

  1. 问题分解:Raft 把共识算法分为三个子问题,分别是领导者选举(leader election)、日志复制(log replication)、安全性(safety)
  2. 状态简化:对算法做出一些限制,减少状态数量和可能产生的变动

复制状态机(Replicated state machine)

相同的初始状态 + 相同的输入 = 相同的结束状态

在 Raft 中,lead 将客户端请求(command)封装到一个个 log entry 中,将这些 log entries 复制到所有 follow 节点,然后大家按相同顺序应用 log entries 中的 command,根据复制状态机的理论,大家的结束状态肯定是一致的。

使用共识算法,就是为了实现复制状态机,从而实现高可用。

状态简化

在任何时刻,每一个服务器节点都处于 leader、follower 或 candidate 这三个状态之一。

相比于 Paxos,Raft 不用考虑状态之间的共存和相互影响,只用考虑状态的转化,极大地简化了算法的实现。

任期

Raft 算法将时间划分成为任意不同长度的任期(term)。任期用连续的数字进行表示。每一个任期的开始都是一次选举(election),一个或多个候选人会试图成为领导人。

RPC

Raft 算法中服务器节点之间通信使用远程过程调用(RPC),并且基本的一致性算法只需要两种类型的 RPC。

  • 请求投票 RequestVote RPC 由候选人在选举期间发起
  • 附加条目 AppendEntries RPC 由领导人发起,用来复制日志和提供一种心跳机制。

服务器之间通信的时候会交换当前任期号;如果一个服务器上的 currentTerm 比其他的小,就会更新为较大的那个。如果一个 candidate 或者 leader 发现自己的任期号过期了,它会立即回到 follower 状态。如果一个节点收到一个包含过期的任期号的请求,会直接拒绝这个请求。

领导者选举

心跳机制

  • Raft 内部存在一种心跳机制,如果存在 leader,那么它就会周期性地向所有的 follower 发送心跳,来维持自己的地位。如果 follower 一段之间没收到心跳,就会开始新的选举
  • 开启一个选举过程后,follower 先增加自己的当前任期号,并转换到 candidate 状态。然后投票给自己,并且并行地向集群中其他服务器节点发送投票请求(RequestVote RPC)

选举结果

  1. 获得超过半数服务器的投票,赢得选举,成为领导者并开始发送心跳;
  2. 另一台服务器赢得选举,接收到对应的心跳,且新 leader 的任期号不小于自己当前的任期号,成为追随者;
  3. 选举超时,没有任何一台服务器赢得选举,自增当前任期,重新发起选举;

对于没有成为 cadidate 的 follower 节点,对于同一个任期,会按照先来先得的原则投出选票。

日志复制

Leader 接收到客户端的指令后,会把指令作为一个新的条目追加到日志中去。

一条日志需要具有三个信息:

  • 状态机指令
  • leader 的任期号
  • 日志号(日志索引)

Leader 并行发送 AppendEntries RPC 给 follower,让它们复制该条目。当该条目被超过半数的 follower 复制后,leader 就可以在本地执行该指令并把结果返回客户端。

我们把本地执行指令,也就是 leader 应用日志与状态机这一步,称作提交

宕机情况

  1. 如果有 follower 因为某些原因没有给 leader 响应,那么 leader 会不断地重发追加条目请求(AppendEntries RPC),哪怕 leader 已经回复了客户端
  2. 如果有 follower 崩溃后恢复,这时 Raft 追加条目的一致性检查生效,保证 follower 能按顺序恢复崩溃后的缺失日志

Raft 的一致性检查:leader 在每一个发往 follower 的追加条目 RPC 中,会放入前一个日志条目的索引位置和任期号,如果 follower 在它的日志中找不到前一个日志,那么它就会拒绝此日志,leader 收到 follower 的拒绝后,会发送前一个日志

​ 3.如果 leader 崩溃,那么 leader 可能已经复制了日志到部分 follower 但还没提交,而选出的新 leader 不具备这些日志,这样就产生冲突。在这种情况下,leader 通过强制 follower 复制它的日志来解决不一致的问题

对于 follower,如果 leaderCommit > commitIndex,那么把 commitIndex 设为 min(leaderCommit,index of last new entry)

安全性

  1. 选举限制:如果投票者自己的日志比candidate的还新,就会拒绝投票请求。
    1. 如果两份日志最后条目的任期号不同,那么任期号大的日志更新
    2. 如果两份日志最后条目的任期号相同,那么日志较长的那个更新
  2. leader宕机处理: 只有leader当前任期内的日志条目才通过计算副本数目的方式来提交;一档当前任期内的某个日志条目以这种方式被提交,由于日志匹配特性,之前的所有条目也会被提交
  3. follower和candidate宕机处理:如果follower和candidate崩溃了,那么后续发送给他们的RequestVote和AppendEnriesRPC都会失败。Raft通过无限重试来处理这种失败,当崩溃的机器重启时,这些RPC就会成功。如果一个服务器在完成了一个RPC,但还没有响应的时候崩溃了,那么它重启之后就会收到同样的请求。
  4. 时间与可用性限制:广播时间 << 选举超时时间 << 平均故障时间

集群成员变更

在需要改变集群配置的时候(如增减节点、替换宕机的机器或者改变复制的程度),Raft可以进行配置变更自动化

集群先切换到一个过渡的配置,称之为联合一致(joint consensus)

第一阶段:leader发起Cold,new , 使整个集群进入联合一致状态。这时,所有RPC都要在新旧两个配置中都达到大多数才算成功。

第二阶段:leader 发起Cnew,使整个集群进入新配置状态。这时,所有RPC只要在新配置中达到大多数才算成功。

扩展与补充

深入理解复制状态机

  • 共识算法的本质是实现复制状态机
  • 构建分布式存储,是为了获取更大的存储容量(Scalability),为了获取更大的存储容量,我们把数据进行分片(Sharding)
  • 更多的机器带来了更高的出错频率
  • 为了容错(Fault Tolerance),我们要对每个分片建立副本(Replication)
  • 为了维持副本之间的一致,就要引入共识算法(Consensus)

根据需要同步的数据量按大小进行分类,分别适合不同类型的共识算法。

  1. 数据量非常小,如集群成员信息、配置文件、分布式锁、小容量分布式任务队列。
    1. 无leader的共识算法(如Basic Paxos),实现有Chubby、Zookeeper等
  2. 数据量比较大但可以拆分为不相干的各部分,如大规模存储系统。
    1. 有leader的共识算法(Multi Paxox、Raft),实现有GFS、HDFS等
  3. 不仅数据量大,数据之间还存在关联
    1. 如Spanner、OceanBase、TiDB等支持分布式事务的分布式数据库。它们通常会对Paxos或Raft等共识算法进行一定的改造,来满足事务级的要求。

Raft 基本特性

共识算法特性:

  1. 共识算法可以保证在任何非拜占庭情况下的正确性
  2. 共识算法可以保证在大多数机器正常的情况下集群的高可用性
  3. 不依赖外部时间来保证日志的一致性

Raft区别于其他共识算法的特性:

  • Strong Leader: 日志只能从leader流向其他服务器
  • Leader election: 使用随机计时器进行领导选举
  • Membership changes:使用共同一致的方法来处理集群成员变更的问题

no-op补丁

一个节点当选leader后,立刻发送一个自己当前任期的空日志体的AppendEntries RPC。这样,就可以把之前任期内满足提交条件的日志都提交了。

目前大部分应用于生产系统的raft算法,都是启用no-op的。

日志压缩机制

随着raft集群的不断运行,各状态机上的log也在不断地累积,我们需要一个机制来安全地清理状态机上的log。

Raft采用了快照技术:每个节点在达到一定条件后,可以把当前日志中的命令都写入自己的快照,然后就可以把已经并入快照的日志都删除了。

leader如何同步日志给落后很多的follower?

raft的策略是直接向follower发送自己的快照

ParallerRaft

ParrallerRaft是阿里云原生数据库PolarDB的底层文件PolarFS对Raft的一种优化的实现

参考资料

Raft

Raft一致性算法笔记 - 简书

你可能感兴趣的:(Go,1024程序员节,算法,golang,架构,京东云)