Raft 是怎么选主和保证日志一致的?
自测:先别看答案
试着用 1~2 分钟把这道题讲出来(说出声或写下要点),再展开对照。
面试怎么回答
Raft 分 Leader、Follower、Candidate 三种角色,按任期划分时间。Follower 超时收不到心跳就升任期发起选举,每个节点每个任期只投一票、且只投给日志不比自己旧的候选人,拿到过半票成为 Leader。写入只走 Leader,日志复制到过半节点即算提交,再应用到状态机;Follower 日志不一致时由 Leader 回退并覆盖。过半保证了任意两次多数派有交集,新 Leader 一定包含所有已提交日志,所以 2N+1 个节点能容忍 N 个故障。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
Kafka 的 KRaft、etcd、TiKV 都基于 Raft。面试官会问:Raft 怎么选出 Leader?怎么保证各节点日志一致?为什么要过半?
三种角色和任期
节点分为 Leader、Follower、Candidate。时间被划分为一个个任期(term),每个任期最多一个 Leader。
选主
- Follower 在一段随机的超时时间内没收到 Leader 心跳,就把任期加一,变成 Candidate,向其他节点请求投票;
- 每个节点在一个任期内只投一票,并且只投给日志不比自己旧的候选人;
- 拿到过半票的候选人成为 Leader,开始发送心跳。
随机超时是为了减少多个节点同时发起选举、票数被瓜分的情况。
日志复制
客户端的写请求只发给 Leader:
- Leader 把命令追加到自己的日志,再并行发给各 Follower;
- 过半节点(包括 Leader 自己)写入成功后,这条日志被认为已提交(committed),Leader 应用到状态机并返回客户端;
- Follower 通过后续的心跳得知提交位置,再应用到自己的状态机。
如果某个 Follower 的日志和 Leader 不一致,Leader 会找到两者最后一致的位置,用自己的日志覆盖 Follower 后面的部分。
为什么是过半
任意两个「过半」集合一定有交集。已提交的日志至少存在于过半节点上;新 Leader 也必须拿到过半选票,而投票规则要求候选人的日志不比投票者旧,所以新 Leader 一定包含所有已提交的日志,提交过的数据不会丢。
这也意味着 2N+1 个节点最多容忍 N 个故障,所以集群通常是 3 个或 5 个节点。