协议和算法篇
Paxos算法
Basic-Paxos
角色
- 提议者:提议一个值,用于投票表决,一般是集群中收到客户端请求的节点(接入和协调)
- 接受者:对每个提议的值进行投票,并存储接受的值,一般来说,集群中所有的节点都在扮演接受者的角色,参与共识协商并接受和存储数据(投票和存储)
- 学习者:被告知投票的结果,接受达成共识的值,存储保存,不参与投票的过程,一般来说是数据备份节点(存储)
如何达成共识
准备阶段(Prepare)
提议者向所有接受者发送只包含提案编号的准备请求(不需要指定提议值):
因为之前没通过任何提案,A,B返回“尚无提案”的响应,并承诺不再响应编号小于1的提案。同理,C承诺不再响应编号小于5的提案。
接着,A,B收到提案5,它们会更新承诺:不再响应编号小于5的提案,C收到提案1,直接不响应。
接受阶段(Accept)
客户端1,2接收到大多数节点的准备响应之后,会分别发送接受请求:
当客户端1接收到大多数的接受者(A,B)的准备响应后,会根据相应中提案编号最大的提案的值,设置接受请求的值。因为这里是个空,所以将自己的提议值作为提案的值,发送接受请求。如果不为空的话,会将所有回复的value 中的最大值作为本次提案的value。(比如自己是[9,6],其他返回[5,7],[4,8],因为5最大,将会使用[9,7]作为接受阶段的数据)
同理,客户端2也会发送自己的[5,7]作为接受请求,当三个接受者收到两个客户端的接受请求后,会进行如下处理:
因为提案编号1 小于三个节点承诺的最小提案编号5,所以提案[1,3]被拒绝,而接受[5,7],也即对7 达成了共识。
如果集群中有学习者的话,接受者通过了一个提案时,会通知给所有的学习者,学习者也会通过该提案并接受该提案的值。当少于一半的节点出现故障的时候,共识协商依然在正常工作。
总结
- Basic Paxos是通过二阶段提交的方式来达成共识的
- 除了共识Basic Paxos还实现了容错,在少于一半的节点出现故障时,集群也能正常工作(大多数)
- 提案编号的大小代表着优先级:
a. 准备阶段,接受者不再接受小于等于已经响应的准备请求的提案编号
b.接受阶段,接受者不再接受小于已经响应的准备请求的提案编号
Multi-Paxos(思想统称)
兰伯特关于Multi-Paxos的思考
Basic Paxos是通过二阶段提交来达成共识的。在准备阶段接收到大多数准备响应的提议者,才能发起接受请求进入第二阶段:
如果直接通过多次执行basic paxos,来实现一系列值的共识,或存在以下问题:
- 如果多个提议者同时提交提案,可能出现因为提案冲突,在准备阶段没有提议者接收到大多数准备响应,协商失败,需要重新协商
- 2 轮 RPC 通讯(准备阶段和接受阶段)往返消息多、耗性能、延迟大
领导者(leader)
领导者节点作为唯一提议者,这样就不存在多个提议者同时提交提案的情况,也就不存在提案冲突的情况了(执行一次Basic Paxos选举出领导者)
注:准备阶段为何可以省掉?
准备阶段的意义,就是发现接受者节点上,已经通过的提案的值。如果在所有接受者节点上,都没有已经通过的提案了,这时候领导者自己指定即可。那么,准备阶段也就没有意义了。
优化 Basic Paxos 执行
优化机制:当领导者处于稳定状态时,省掉准备阶段,直接进入接受阶段。那么就不会存在提案冲突,同时省掉了准备阶段,减少了往返的消息数,提升了性能,降低了延迟。
chubby 的 Multi-Paxos实现
- 引入主节点,由basic paxos投票选举产生
- 主节点会不断续租,延长租期
- 主节点正常时,省掉了准备阶段,直接进入接受阶段
- 为了实现强一致性,读写操作都只能在主节点上执行。限制了集群处理写请求的并发能力,约等于单机
主节点接收到写请求之后,将数据发送给所有的节点,并且在大多数节点接受了这个写请求之后,响应给客户端成功:
主节点接收到读请求之后,直接查询本地数据,然后返回给客户端:
内容小结
- 通过减少非必须的协商步骤来提升性能,非常有效,google的quic协议也是通过减少TCP、TLS的协商步骤,优化HTTPS性能
- 实现Multi-Paxos算法,最大的挑战是如何证明它是正确的,推荐优先考虑Raft算法