世上有很多选举算法,但最完美的莫过于Paxos,但把paxos用于master选举与用于value的选举有用众多不同之处,主要一点是二者执 行频率不同。value选举需要频繁、高密度地执行paxos算法,每instance选举一个value。因此,原生的paxos算法无法满足高性能的 要求;另一个问题是,paxos理论上存在活锁,lamprot推荐大家通过选举Leader避免这个问题。
所有的实现都指向一个Leader/Master,但Master的选举又该如何实现?如果继续采用paxos那性能和活锁又该如何解决?这个似乎 是个递归问题,用paxos解决paxos遇到的问题。能实现吗?能,Keyspace的PaxosLease算法给出了答案。
1. Lease
Master选举的过程是这样的:从众多的Node中选择一个作为Master,如果该Master一致alive则无需选举,如果master crash,则其他的node进行选举下一个master。选择正确性的保证是:任何时刻最多只能有一个master。
逻辑上Master更像一把无形的锁,任何一个节点拿到这个锁,都可成为master,所以本质上Master选举是个分布式锁的问题,但完全靠锁 来解决选举问题也是有风险的:如果一个Node拿到了锁,之后crash,那锁会导致无法释放,即死锁。一种可行的方案是给锁加个时间(Lease),拿 到锁的Master只能在Lease有效期内访问锁定的资源,在Lease timeout后,其他Node可以继续竞争锁,这就从根本上避免了死锁。
Master在拿到锁后,如果一直alive,可以向其他node”续租“锁,从而避免频繁的选举活动。关于Lease更多的请参考:http://research.microsoft.com/en-us/um/people/blampson/58-consensus/webpage.html
2. Master选举
Master选举与value选举存在几个不同的地方:
- 执行频率低(正常情况下,Node失败概率不会太高)
- 无Paxos的持久化,重启后状态丢失
为了保证选举正确性,还要保证:
- 保证算法纯正性,不依赖与其他算法
3. PaxosLease
PaxosLease是Kespace开发的基于Paxos、Lease的Master选举算法,算法的角色与Paxos一样,也分Proposer、Acceptor、Learn,各角色的行为也与paxos基本一致,但除下面两个重要的区别:
- 不要求acceptor持久化数据
- 不存在Leader
PaxosLease把Paxos的accept阶段重新命名为propose,因此存在prepare、propose2个阶段。
再引入几个符号:
- T:每个Master的Lease时间(秒)
- M:全局Lease时间,要确保 M > T
- ballot number:每次发送的proposal编号
算法过程
- proposer启动定时器Timer1,等待T秒便超时
- proposer向acceptor发送(ballot number,proposer id,T)
- acceptor接收到prepare消息后,判断:
- 如果msg.ballot_number < local.promisedBallotNumber,则发送拒绝消息
- 否则,发送accept,包含的内容已经promise的最大编号和T
- proposer接收acceptor的response
- 如果多数派accept,则进入promise阶段
- 否则,随机等待一段时间,提高编号重启prepare过程
- proposer执行promise阶段
- 如果prepare阶段接收的value不为空,则终止promise
- 否则,发送(ballot number,proposer id ,T)
- acceptor接收到promise请求
- 如果msg.ballot_number < local.promisedBallotNumber,则回应拒绝消息
- 否则,启动定时器Timer2,等待T秒超时
- proposer接收acceptor的response
- 如果接收到多数派的回应
- 删除Timer1
- 启动extend Timer3,等待时间T1 < T
- 发送Learn消息,转入8
- 否则,重启prepare过程
- 如果接收到多数派的回应
- Learn接收到proposer的learn消息
- 停止Timer1
- 重新启动Timer1
因为proposer、acceptor、learn三位一体,所以上述提到的Timer1、Timer2、Timer3其实位于同一Node,同一进程。
- Timer1:等待时间T,超时便发起prepare请求
- Timer2:等待时间T,超时便清空本次paxos instance状态,继续下一次
- Timer3:等待时间T1 < T,超时便执行prepare
Timer1运行于所有的Node,任何一个Node的Timer1超时便发起prepare请求
Timer2仅运行与参与选举过程的Node,如果超期则清空本次选举状态
Timer3仅运行于获得lease的节点,目的是在lease超期之前续租
4. Paxos
移除lease相关部分,上述过程其实就是一次paxos instance,但第5步与原始paxos算法稍有不同:
- 原始的paxos规定,如果接收的value v不为空,则使用v继续accept阶段,以保证每次选举仅选出一个决议;
- PaxosLease选举的目的是使其他node接受自己作为master,当接收的value不为空时,自己应该退出选举,而不是继续提交其他Node的value。也就是说,只要当前node知道其他Node可能会优先自己成为master,则退出选举,成就他人。
因为每个节点都始终运行着Timer1,只要超时便开始prepare,因此paxosleas不存在静态死锁问题,但动态死该怎么解决?之前关于 paxos算法的介绍得知,在paxos中存在活锁,也即动态死锁,表现为在prepare阶段2个proposer会转而提出更高编号的 proposal,PaxosLease提供的解决办法就是在失败重新提交proposal时随机等待一段时间,因为各Node等待时间的不一致,只要运 行足够长的时间,活锁总能避免。
传统的Paxos是否可以采取这样的方法避免活锁?答案是否定的,之前说过,value选举的频率高、延迟低,不能容忍提交proposal时等待几秒。而Master选举因为频率低,却可以容忍上述方法带来的性能损失。
因为运行PaxosLease的所有节点都没有持久化,当Node重启时所有的状态都会丢失,这样重新加入的节点可能会扰乱前面的paxos过程, 因此强制Node在重新加入前必须等待M秒,因为M > T,所以只要等待M秒便可以越过本次paxos选举。其实即使等待M秒,也不能从理论上完全保证PaxosLease能正确执行,要搞清楚这个我问题,我 们必须知道新加入的Node是如何扰乱了paxos过程:
假如存在三个Acceptor A、B、C,已经promised的最高编号分别为A =(1),B =(1),C =(1);存在两个proposer P1,P2
- 初始状态A=(1)、B=(1)、C=N/A# P1 提交编号(3,v3)到A、B成功完成了prepare、accept阶段,编号3,v1被选为最终决议
- B宕机重启,状态丢失回到初始状态、A宕机、C重启,则A=N/A、B =(1)、C=(1)# P2提交编号(2,v2)到B、C也可成功完成preapare、accept阶段,编号2,v2被选为最终决议
存在v2、v3两个最终决议,违反了paxos的安全属性。如果B重启能记住之前的状态B=(3,v3),P2则不会提交成功,从而能保证paxos的安全属性。这个错误发生要几个条件:
- 节点重启前作为多数派回应了prepaer、accept消息
- 其他节点也重启,并与当前节点构成最少多数派
- 之前的instance未结束,又回答了其他proposer的prepare、accept消息
反观M秒的设定,其实等待多少秒并不重要,重要的是重加入后不能立即参加paxos过程。在实际中因为选举的过程非常快,节点重加入的过程也可监控,这些理论上的错误是很难发生的。
Catch Up机制是否适用于PaxosLease?
在讨论paxos的实现时,曾提到如果节点磁盘失败,当重新加入时需要使用catch up,节点参与paxos过程,但不做任何回应,直至一次paxos过程完成。
但这个并不适合于PaxosLease,因为master选举的频率很低,如果等待一次paxos过程完成,节点要等很长的时间才能重新加入。
5. 正确性证明
PaxosLease是由3个精妙配合的Timer来工作的,Paxos算法的正确性是显然的,PaxosLease会不会在引入Timer之后改变其安全假设?请看下面简短证明:
首先引入几个符合:
- b:ballot number
- tstart :proposer启动Timer1的时间
- tnow :proposer在prepare阶段接收到多数派响应空value的时间
- tacquire :proposer在propose阶段接收到多数派的accept时间
- A1:在prepare阶段回应proposer的多数派
- A2:在propose阶段回应proposer的多数派
假如proposer p已经获得了lease,则在tend =tstart +T之内,其他proposer不会获得多数派acceptor响应空value,也即其他proposer不会获得lease。
Part1:不存在任何proposer q,编号b1<b,在[tacquire ,tend ]之间获得lease
假如q也在此期间获得了lease,则存在多数派A4在propose阶段回应了q,令a是A2和A4的公共成员,因为b1<b,a必定先接 受了q的proposal,然后发送回应给p,因为p在propose阶段接收到了空value,说明q的Timer2已经超时,因此q的timer也肯 定超时(b1<b,q的timer启动的更早),因此p、q的lease并无重叠
Part2:不存在任何propose q,编号b1>b,在[tacquire ,tend ]之间获得lease
证明与上类似。
6. 结论
其实PaxosLease的正确性是有Paxos算法保证的,PaxosLease只是在Paxos的基础上限定了一个时间。在时间T之内,任何Node都不能申请lease,因此master宕机后重新选择master的最大时间为T,也即服务不可用的最大时间为T。
T设的过小会减少服务不可用的时间,但会产生更多的内部消息;设的过大内部消息减少,但会导致更长的宕机时间。
PaxosLease是一个节点之间不存在依赖的简洁的选举算法,就像其论文所说每个节点都有两个状态:
- 我不是master,也不知道谁是master
- 我是master
正因为如此,PaxosLease的缺点就是无法做到负载均衡,无法按权重选择master。