为什么是paxos
最近又看了次 paxos made simple,每次看完总觉得理解了它,但不久之后又将它忘得一干二净,因此决定这次好好梳理下。本文还参考了微信对于paxos 的实现的相关文章(见 phxpaxos)。
本文主要从paxos是如何执行的,已经如果不这么执行,会有什么样的问题来描述paxos,这个思路是我觉得比较好接受的方式。至于paxos的理论证明,在 The Part-Time Parliament 和 paxos made simple 已经将的很清楚了,有兴趣可以去看论文。
paxos基本定义
概论:paxos要解决的问题是,在多台机器确定一个值。也就是说完整跑完paxos,可以保证在这个集群里,最终总有个时刻,所有存活的机器保留的这个值都相同(至于得到一个不变量,在工程上有什么用,这里就不加讨论了)。
paxos里面有几个角色:
- proposer:提议者,接收客户端的请求,发起一个请求。
- acceptor:接受者,决定是否接受proposer的提议。
对于一个提议,包含:
- proposal number:提议号,全局唯一,且各自的机器需要保证自己的proposal number是递增的。实现可以是:时间戳加ip或者提前分好各自的提议段(第一台机子:1,101,201,第二台机子:2,102,202)。
- proposal value:提议的值。
paxos的执行流程
第一阶段(prepare request)
- proposer 选择一个 proposal number ,然后将它发送给大多数 acceptor (大多数:对于一个固定集合来说,大多数基本就是大于一半的数量。更精确的定义的是,两个大多数集总有一个共同元素)。这个请求被称为:prepare request 。
- 对于 acceptor ,如果它收到一个 proposal number 为 n 的 prepare request,那么倘若这个 acceptor 没有回复过 proposal number 大于 n 的 prepare request ,则该 acceptor 需要回复这个 prepare request,并承诺不再 accept 任意 proposal number 小于 n 的 prepare request,如果在这之前该 acceptor accept 过任何 proposal,则还必须带上 accept 过的拥有最大 proposal number 的 proposal (含 proposal number 和 value)。
第二阶段(accept request)
- 如果 proposer 收到大多数 acceptor 对于 proposal number 为 n 的 prepare request 的响应,那么该 proposer 就可以向 acceptor 发起 accept request,proposal number 为 n,值为 v (其中,如果 acceptor 响应中带有 proposal,则 v 为 proposal number 最大的那个 proposal 值,否则 v 可以为任意值)
- acceptor 收到 accept request 后,如果这个proposal 不违背该 acceptor 做出的承诺(在 prepare request 阶段,不再接受 proposal number 小于 n 的承诺),则 acceptor 必须 accept 这个请求。
当大多数的 acceptor 都回应了这个 accept request,那么完整的 paxos 就已经跑完了,唯一的一个值已经被choose了。
为什么paxos要这么麻烦
不用两个阶段可以么
paxos 的强大之处在于即使有多个 proposer 并行地提出请求,也能保证集群最终只会 choose 到同一个值,而且只要集群的大多数机器不宕机,服务是可以继续下去的。
如果各个 proposer 直接提出 accept request ,那么很容易就出现这样的情况:有4台机器ABCD,A和B提出了propose,很可能就出现了AC两台机器同意了A的请求,BD两台机器同意B的请求,最终将无法得到一个结果。
话虽这么说,但 paxos 其实也会协商得不到一个值的情况:A向大多数机器发送了 prepare request,proposal number 为 1,并得到了大多数机器的响应;与此同时 B 也向大多数机器发送了 prepare request ,proposal number 假设为 2,那么这时 A 再发送 accept request 的时候,就会被这些机器拒绝(它们已经承诺不接受proposal number 小于2 的请求),所以A 重新发送 prepare request , proposal number 为101,之后 B 再来发送 accept request 的时候就被拒绝,所以 B 又发送proposal number 为102的 prepare request,如此往复,就永远得不到一个共同的值。
由于 paxos 存在着上述这种情况,所以如果有个 leader ,由这个 leader 来提出 propose ,就不会有上述的情况。那么这个 leader 是怎么确定的,在 paxos made simple 的论文里面并没有提到。不过可以看出,paxos 对这个 leader 并没有做出任何限制,也就是说,即使在某个时刻存在着两个 leader ,同样也不会影响到 paxos 来确定出最终的一个值。因此 leader 的选举就变得不是特别复杂,例如利用一些简单的策略:一旦收到其他机器的 accept request ,就在一段随机时间内,停止进行 paxos 的 prepare request。由于这是随机时间,所以最终总会有台机器脱颖而出,不断进行 prepare request,然后 accept request ,这使得其他机器又进行一段时间的停止 prepare request,所以该机器又能顺利通过 prepare request,再 accept request。这样,一个 leader 就自然而然地产生了。
那 paxos 来选举 master 又是怎么回事
master 的一个重要特点是:集群在任意时刻,最多只能有一个 master 。也就是说,宁可没有 master ,也不能出现多个 master 的情况。说起 master ,要稍微说下 paxos 选出一个值后有什么用,可以这么想,paxos 可以选出一个值,那么顺序运行多个 paxos 实例,就可以做到在集群中,各个机器都维护一个状态机,顺序执行 paxos 的结果可以作为这个状态机的输入,那么就能保证集群中所有的状态机都能得到一个最终一致性(这也就是基本的 multi-paxos 的模型)。而 master 的作用就体现出来了,如果有个 master 进行读写,那么从这个 master 总能读出最新的数据。
至于 master 怎么选,简单点说就是发起一个 paxos 的过程,各台机器的proposal value 就是自己的节点信息,也就是各个机器都认为自己是 master ,最终确定下来一个 value,也就确定了 master 。当然也没这么简单,如果 master 挂了又该怎么办?其实不需要主动去判断 master 是否挂了,只需要给 master 一个任期,任期结束后,master 主动去续约。只要 master 在任期结束前,提前去续约,就总是能续约成功的。 既然引入了任期的概念,也就是说,需要用多个 paxos 实例来确定,也即是 multi-paxos ,但有机器与 master 失联,没有学到 master 的续约的请求后,重新发起次 paxos,由于 paxos 实例落后于 master 的,自然不会得到大多数机器的接受,进而可以学到 master 是谁。
不同的 proposer 的 proposal number 一样可以么
不可以。paxos 对 proposal number 是有一定的要求的:proposal number 在所有机器中必须是唯一的,而且各自的机器必须是递增的。从 paxos 的过程也可以看出,在 prepare request 阶段,是只利用 proposal number 来进行提议的,不需要用到提议的值,承诺也是针对 proposal number ,如果存在不同机器的 proposal number 是一样的,而它们提议的值却不一定一样,这就违背了最终所有机器确定唯一的值的初衷了。