算法设计
算法模型
从系统模型的角度,我们又可以将分布式共识算法(Distributed Consensus Algorithms)看做解决如何在多个节点之间复制

每个节点都会包含一个状态机实例与日志,但是我们希望呈现给客户端使用者的仿佛只有单个可信赖的、能容错的状态机;提供的服务的可用性与准确性不会受到集群中节点的失败而影响。每个状态机都能够从其自身的日志中读取指令,譬如以set x to 3
这样子的。共识算法也就是为了保证这些节点上的日志内容的一致性,共识算法必须保证如果某个节点上的第set x to 3
,那么其他所有节点上不会出现该序列位不一样的情况。最终,所有的状态机都会以相同顺序执行指令,并得到一致的状态序列。

上述介绍可以形式化如下:一个或多个节点可以提议(propose)某些值,而共识算法决定(decides)采用其中的某个值。在这种形式下,共识算法必须满足以下性质:
- 一致同意(Uniform agreement
) :没有两个节点的决定不同。 - 完整性(Integrity
) :没有节点决定两次,一致同意和完整性属性定义了共识的核心思想:所有人都决定了相同的结果,一旦决定了,你就不能改变主意。 - 有效性(Validity
) :如果一个节点决定了值v ,则v 由某个节点所提议。有效性属性主要是为了排除平凡的解决方案:例如,无论提议了什么值,你都可以有一个始终决定值为null 的算法;该算法满足一致同意和完整性属性,但不满足有效性属性。 - 终止(Termination
) :由所有未崩溃的节点来最终决定值。如果你不关心容错,那么满足前三个属性很容易:你可以将一个节点硬编码为“独裁者”,并让该节点做出所有的决定。但如果该节点失效,那么系统就无法再做出任何决定。事实上,这就是我们在两阶段提交的情况中所看到的:如果协调者失效,那么存疑的参与者就无法决定提交还是中止。终止属性正式形成了容错的思想。它实质上说的是,一个共识算法不能简单地永远闲坐着等死;换句话说,它必须取得进展。即使部分节点出现故障,其他节点也必须达成一项决定。
共识的系统模型假设,当一个节点“崩溃”时,它会突然消失而且永远不会回来。在这个系统模型中,任何需要等待节点恢复的算法都不能满足终止属性。特别是,
因此终止属性取决于一个假设,不超过一半的节点崩溃或不可达。然而即使多数节点出现故障或存在严重的网络问题,绝大多数共识的实现都能始终确保安全属性得到满足一致同意,完整性和有效性。因此,大规模的中断可能会阻止系统处理请求,但是它不能通过使系统做出无效的决定来破坏共识系统。大多数共识算法假设不存在拜占庭式错误,正如在“拜占庭式错误”一节中所讨论的那样。也就是说,如果一个节点没有正确地遵循协议(例如,如果它向不同节点发送矛盾的消息
共识算法和全序广播
最著名的容错共识算法是视图戳复制(VSR, viewstamped replication
请记住,全序广播要求将消息按照相同的顺序,恰好传递一次,准确传送到所有节点。如果仔细思考,这相当于进行了几轮共识:在每一轮中,节点提议下一条要发送的消息,然后决定在全序中下一条要发送的消息。所以,全序广播相当于重复进行多轮共识(每次共识决定与一次消息传递相对应
- 由于一致同意属性,所有节点决定以相同的顺序传递相同的消息。
- 由于完整性属性,消息仅发送一次而不会重复。
- 由于有效性属性,消息不会被损坏,也不能凭空编造。
- 由于终止属性,消息不会丢失。
视图戳复制,
单领导者复制和共识
在单领导复制中,它将所有的写入操作都交给主库,并以相同的顺序将它们应用到从库,从而使副本保持在最新状态。这实际上不就是一个全序广播吗,为何我们并没有导致共识问题呢?答案取决于如何选择领导者。如果主库是由运维人员手动选择和配置的,那么你实际上拥有一种独裁类型的“共识算法”:只有一个节点被允许接受写入(即决定写入复制日志的顺序
时代编号和法定人数
一些数据库会自动执行领导者选举和故障切换,如果旧主库失效,会提拔一个从库为新主库,这使我们向容错的全序广播更进一步,从而达成共识。但是还有一个问题。我们之前曾经讨论过脑裂的问题,并且说过所有的节点都需要同意是谁领导,否则两个不同的节点都会认为自己是领导者,从而导致数据库进入不一致的状态。因此,选出一位领导者需要共识。但如果这里描述的共识算法实际上是全序广播算法,并且全序广播就像单主复制,而单主复制需要一个领导者,那么这样看来,要选出一个领导者,我们首先需要一个领导者。要解决共识问题,我们首先需要解决共识问题。我们如何跳出这个先有鸡还是先有蛋的问题?
迄今为止所讨论的所有共识协议,在内部都以某种形式使用一个领导者,但它们并不能保证领导者是独一无二的。相反,它们可以做出更弱的保证:协议定义了一个时代编号(epoch number
每次当现任领导被认为挂掉的时候,节点间就会开始一场投票,以选出一个新领导。这次选举被赋予一个递增的时代编号,因此时代编号是全序且单调递增的。如果两个不同的时代的领导者之间出现冲突(也许是因为前任领导者实际上并未死亡
相反,它必须从
这一投票过程表面上看起来很像两阶段提交。最大的区别在于,