2 minutes
Raft Paper
摘要
raft: 比paxos简单的共识算法, 相比paxos, 有什么特殊的点:
- 共识问题简化成三个独立的问题: leader election、log replication、safety[logIndex->log唯一且不可变]
- 不允许类似paxos的乱序提交
- 使用 Randomization 算法简化leader election问题.
- 使用term概念代替原子钟的概念
本身, paxos 就存在一些问题: 1. 难以理解 2. 不具备良好的工程实践能力, multi-paxos 缺乏实现细节. Chubby 实现了类似 paxos 的算法, 但是不公开
细节
1. leader选举
-
raft中有三个角色: leader、candidate、follower, 只有 candidate可以竞选leader, 竞选leader的时候, 竞选特点如下:
- 需要得到包括自己在内一半以上的投票
- Candidate的term比voter大, 在相同的情况下, candidate的logEntry的sn比voter大
这里存在问题: 当一个节点被隔离了, 会出现不断投票给自己的情况, 导致term非常大, 隔离的节点重新加入集群后, 会触发集群多次选举, 直到集群中的节点的term和被隔离的节点一样大
-
raft在leader当选成功后, 会执行下面几个特点和操作:
- leader立即发送一个 no-op entry, 保证leader commit index是最新的, 使整个集群快速进入可读状态.
- 当follower 发送自身的commit index 比leader大, 会进行删除操作, 删除本地 leader commit index 之后的内容
- leader不能提交之前term的entry, 必须当entry已经得到集群节点半数的响应, 才能将之前的entry提交
-
leader存活期间, 有下面几个特点
- 有心跳租约的概念, 心跳租约时间内, 请求不能被处理
- 存活期间, leader 与 follower需要通过心跳维持关系
- follower 在指定时间内收不到心跳的情况下, 会重新发起选举
2. 内容复制
-
写入的执行流程如下:
- client -> leader: 客户端请求leader写入kv
- leader append 本地日志(commitlog)
- leader 并行发送日志给 follower
- follower收到日志, 写入本地 commit log, 并 apply 本地的 FSM, 返回成功给 leader
- leader收到follower超过半数以上的成功响应, 本地apply 日志到 FSM.
写入流程中有几个关键的术语:
- commit log: 提交的日志, 所有的日志都会先写入 commit log.
- fsm: 有限状态机, 确认达成一致的内容会写入fsm.
这两者在 follower层基本上顺序执行的, 在 append commit log后立即 apply fsm. 在leader层, append commit log后, 需要并发发送请求给follower, 需要半数以上follower返回成功响应后, 才能写入 fsm.
-
读取的执行流程如下:
- client -> leader: 客户端请求leader写入kv
- leader 通过 lease 检查自己是否是 leader,
- 检查是leader的情况下, 检查本地 apply index 和 客户端的 index, apply index大的话, 读取本地的状态机的数据 + apply index返回
- 不是leader的情况下, 就请求leader获取最新的 apply index, 和 客户单的index 比较, apply index 大的话, 读取本地的状态机的数据 + apply index 返回
通过流程发现, 有client index的概念, 这个是server(leader/follower)返回的apply index. 除此之外, server(leader/follower)都是从本地的fsm中返回数据的, 而不像其他的方式那样,follower从leader中获取数据.
3.异常情况考虑
-
写入的数据在半数响应后, 还没来得及响应给客户端, 就挂了 在重新leader选举后, 数据依旧保持在 raft中, 但是客户端因为之前没有收到响应, 会认为操作失败, 所以, 客户端必须得重试内容, 不然会出现 客户单认为失败,但是存储层成功的情况 那么, 客户端重试请求, 会导致重复内容存储吗? 不会, client需要给每一个请求添加一个唯一的编号, 服务端保证幂等, 一个编号的请求值处理一次
-
是否存在新选举的leader日志是不全的? 以至于leader切换内容丢失? Leader Completeness Property 保证了 leader是有所有提交的信息的, leader刚当选成功, 会发送 no-op entry来保证自己是新的, 如果不是最新的, 会被拒绝掉.
其他:
-
三种时间
广播时间(broadcastTime) « 选举超时时间(electionTimeout) « 平均故障间隔时间(MTBF)
广播时间指的是从一个服务器并行的发送 RPC请求 给集群中的其他服务器并接收响应的平均时间, 论文中指出, 关闭时间在 0.5ms 到 20ms的范围, 选举时间在 10ms 到 500ms范围.
-
日志压缩
为了避免日志无限增长的问题, 使用快照方式, 将之前的日志和快照删除, 只保留最新的快照
-
群成员管理
通过引入 joint consensus的概念, 实现了过渡期的概念, 只有 拥有c-old+c-new 的server才有可能被选举成leader, 然后c-old+c-new的日志提交后, leader在使用相同的方式提交 c-new的log entry, 避免 c-old 和 c-new的分裂.
但是, etcd/tikv并没有这么做, 他们通过引入 learner 的概念解决, learner 不能进行投票, 只能够同步日志, 当日志能够跟上follower, 可以指定learner提升到 follower.
优化&实现
- pingcap raft: 实现了 batch&pipeline leader append、 append log parallelly、Asynchronous Apply、Asynchronous Lease Read
- etcd/tikv 使用learner而不是 joint consensus来保证成员更新的可靠性
参考信息:
- (官网)[https://raft.github.io/]
- (论文)[https://raft.github.io/raft.pdf]
- (可视化动图)[http://thesecretlivesofdata.com/raft/]
- (raft大规模使用)[https://zhuanlan.zhihu.com/p/23872141]
288 Words
2019-04-03 20:31 +0800