同步模型(Synchronous model): 在同步模型中,所有节点之间的消息通信都存在一个已知的延迟上界,并且不同节点处理事务的相对速度差值有一个已知上界。同步模型是一个非常理想的通信模型,在现实生活中几乎不可见,但是在分布式系统的理论研究中却发挥着及其重要的作用,许多早期的分布式一致性算法都是在同步网络假设下设计的。 异步模型(Asynchronous model): 在异步模型中,上述的假设上界都不存在,因此异步模型比较符合现实的互联网环境。异步与同步相比,是一种更通用的情况。一个适用于异步系统的算法,也能被用于同步系统,但是反过来并不成立。在异步模型中设计一个正确的共识算法已经被证明是不可能的(FLP不可能理论)。我们正在使用主的IP网络属于这种。 部分同步模型(Partial Synchronous model): 部分同步模型是界于同步模型与异步模型之间的一种通信模型,于1988年由Dwork, Lynch等人在论文[1]中提出。该模型中假设存在一个全局稳定时钟GST(Global Stabilization Time),在GST之前整个系统可能处于异步状态,但是在GST之后,整个系统可以恢复到同步状态。部分同步模型的时序假设比较贴合现实世界中对共识算法的需求,即共识总是可以在同步状态下完成,然而一旦网络出现问题,共识可能会进入一段时间的阻塞,直至网络恢复正常。
FLP 不可能原理:允许节点失效(即便只有一个)的最小化异步模型系统中,不存在一个可以解决一致性问题的确定性共识算法(No completely asynchronous consensus protocol can tolerate even a single unannounced process death)。
提出并证明该定理的论文《Impossibility of Distributed Consensus with One Faulty Process》是由 Fischer、Lynch 和 Patterson 三位科学家于 1985 年发表,该论文后来获得了 Dijkstra(就是发明最短路径算法的那位计算机科学家)奖。
FLP 不可能原理告诉我们,不要浪费时间去试图为异步分布式系统设计面向任意场景的共识算法。
CAP 原理:分布式系统无法同时确保一致性(Consistency)、可用性(Availability)和分区容忍性(Partition),设计中往往需要弱化对某个特性的需求。
一致性、可用性和分区容忍性的具体含义如下:
- 一致性(Consistency):任何事务应该都是原子的,所有副本上的状态都是事务成功提交后的结果,并保持强一致;
- 可用性(Availability):系统(非失败节点)能在有限时间内完成对操作请求的应答;
- 分区容忍性(Partition):系统中的网络可能发生分区故障(成为多个子网,甚至出现节点上线和下线),即节点之间的通信无法保障。而网络故障不应该影响到系统正常服务。
CAP 原理认为,分布式系统最多只能保证三项特性中的两项特性。
比较直观地理解,当网络可能出现分区时候,系统是无法同时保证一致性和可用性的。要么,节点收到请求后因为没有得到其它节点的确认而不应答(牺牲可用性),要么节点只能应答非一致的结果(牺牲一致性)。
由于大部分时候网络被认为是可靠的,因此系统可以提供一致可靠的服务;当网络不可靠时,系统要么牺牲掉一致性(多数场景下),要么牺牲掉可用性。
ACID,即 Atomicity(原子性)、Consistency(一致性)、Isolation(隔离性)、Durability(持久性)四种特性的缩写。
ACID 也是一种比较出名的描述一致性的原则,通常出现在分布式数据库等基于事务过程的系统中。
具体来说,ACID 原则描述了分布式数据库需要满足的一致性需求,同时允许付出可用性的代价。
- Atomicity:每次事务是原子的,事务包含的所有操作要么全部成功,要么全部不执行。一旦有操作失败,则需要回退状态到执行事务之前;
- Consistency:数据库的状态在事务执行前后的状态是一致的和完整的,无中间状态。即只能处于成功事务提交后的状态;
- Isolation:各种事务可以并发执行,但彼此之间互相不影响。按照标准 SQL 规范,从弱到强可以分为未授权读取、授权读取、可重复读取和串行化四种隔离等级;
- Durability:状态的改变是持久的,不会失效。一旦某个事务提交,则它造成的状态变更就是永久性的。
与 ACID 相对的一个原则是 eBay 技术专家 Dan Pritchett 提出的 BASE(Basic Availability,Soft-state,Eventual Consistency)原则。BASE 原则面向大型高可用分布式系统,主张牺牲掉对强一致性的追求,而实现最终一致性,来换取一定的可用性。
对于分布式事务一致性的研究成果包括著名的两阶段提交算法(Two-phase Commit,2PC)和三阶段提交算法(Three-phase Commit,3PC)。
两阶段提交算法最早由 Jim Gray 于 1979 年在论文《Notes on Database Operating Systems》中提出。其基本思想十分简单,既然在分布式场景下,直接提交事务可能出现各种故障和冲突,那么可将其分解为预提交和正式提交两个阶段,规避风险。
- 预提交(PreCommit):协调者(Coordinator)发起执行某个事务的执行并等待结果。各参与者(Participant)执行事务但不提交,反馈是否能完成,并阻塞等待协调者指令;
- 正式提交(DoCommit):协调者如果得到所有参与者的成功答复,则发出正式提交指令,否则发出状态回滚指令。
两阶段提交算法因为其简单容易实现的优点,在关系型数据库等系统中被广泛应用。当然,其缺点也很明显。
第一阶段时,各参与者同步阻塞等待时无法处理请求,会导致系统性较差;
存在协调者单点故障问题,最坏情况下协调者总是在第二阶段故障,无法完成提交;
可能产生数据不一致的情况。例如第二个阶段时,协调者将正式提交请求发给部分参与者后发生故障。
三阶段提交算法针对两阶段提交算法第一阶段中参与者阻塞问题进行了优化。具体来说,将预提交阶段进一步拆成两个步骤:询问提交和预提交。
完整过程如下:
- 询问提交(CanCommit):协调者询问参与者是否能进行某个事务的提交。参与者需要返回答复是否准备好,但无需执行提交,也无需阻塞。这就避免出现参与者被阻塞的情况;
- 预提交(PreCommit):协调者检查收集到的答复,如果全部为真,则发起执行事务请求。各参与参与者(Participant)需要执行事务但不提交,并反馈能否完成。注意此时说明所有参与者都已经处于准备好状态。;
- 正式提交(DoCommit):协调者如果得到所有参与者的成功答复,则发出正式提交请求,否则发出状态回滚指令。本阶段时,如果参与者一直收不到请求,则超时后继续提交。
三阶段提交主要解决了阻塞问题和协调者单点故障问题。第三阶段时,**如果参与者无法及时收到协调者的消息,可以在超时后自动进行提交。**但是当协调者发出的回滚消息未被部分参与者收到时,会出现不一致的情况。
其实,无论两阶段还是三阶段提交,都只是一定程度上缓解了提交冲突的问题,并无法确保系统的一致性。首个有效的共识算法是后来提出的 Paxos 算法。
区块链是一个分布式的系统,分布式系统要解决的首要问题是如何保证各个节点的数据保持一致。共识算法就是用来解决这个问题的。共识算法的发展历史可以追溯到计算机科学的早期,随着分布式系统和网络的兴起,共识算法才逐渐成为一个重要的研究领域。
1978年,Lamport首次提出了一个基于选举机制的思想,这是Paxos算法的雏形。然而,由于当时的技术和概念还不够成熟,这个思想并没有引起广泛关注。在1982年,Lamport以及他的合作者提出了拜占庭将军问题,这个问题探讨了在分布式系统中存在崩溃或恶意节点的情况下如何达成共识的问题。这个问题引发了对共识算法的深入研究。直到1990年,Lamport正式提出了Paxos算法,这是分布式系统中最为知名和广泛使用的共识算法之一。Paxos算法解决了如何在存在故障节点的情况下达成共识的问题。不过,由于Paxos算法过于复杂,导致包括学术界在内的很多人还是无法理解其核心思想。然后,在2001年,Lamport发布了一篇题为"Paxos Made Simple"的论文,简化了Paxos算法的描述,使其更易理解和实现。Paxos算法随后在分布式数据库、分布式文件系统等领域得到了广泛应用。2014年,Ongaro和Ousterhout提出了Raft算法,它是另一种用于分布式共识的算法。Raft算法的设计目标是更易理解和实现,成为了与Paxos算法相媲美的共识算法。Raft共识算法随后被广泛应用在分布式数据库、分布式文件系统以及像Kubernetes这样容器编排平台,Fabric作为最具代表性的企业级开源联盟链项目,也引入了Raft共识算法。也是随着区块链技术的兴起,共识算法在加密货币领域得到了广泛应用。例如,比特币使用了工作量证明(Proof of Work)算法来达成共识,以确保交易的安全性。另外,基于拜占庭容错的共识算法如Practical Byzantine Fault Tolerance(PBFT)也在分布式系统中得到了广泛应用。随后各大公链也纷纷引入经过改进或新提出的各种共识算法,比如基于权益的POS算法,EOS提出的DPOS算法,Cosmos应用的Tendermint共识算法,HotStuff算法等等。
正如前面提到的,Paxos算法虽然是共识算法的鼻祖,但是由于其过于复杂,难以应用在工业领域,一般是使用其改进后的算法,例如Raft。所以后面会主要介绍比较具有代表性且应用比较广泛的共识算法,例如Raft, PBFT以及Tendermint算法等。
在共识算法中,"Safety"(安全性)和 "Liveness"(活性)是两个关键概念,用于描述算法的性质和保证。
Safety(安全性): 安全性指的是系统在一致性方面的保证。在共识算法中,安全性确保系统不会产生错误的结果,即使在存在故障或恶意节点的情况下也能保持正确性。在安全性中,有两个主要的性质:
- 一致性(Consistency):安全性确保如果一个节点(正确节点)接受了一个值,那么其他节点也会接受相同的值,即所有正确节点达成一致。这就意味着系统不会出现分歧或不一致的状态。
- 合法性(Validity):安全性还确保只有一个合法的提议值被接受,而不会接受来自恶意节点的虚假提议。
Liveness(活性): 活性指的是系统在时间上的进展和反应能力。在共识算法中,活性确保系统最终会做出决策或达到一致状态,即使在存在故障或网络延迟的情况下也能够继续前进。活性的主要性质是:
- 终止(Termination):活性确保在一定时间内,算法最终会完成,并且最终会达成共识。
在共识算法设计中,安全性和活性是相互竞争的目标。提高安全性可能会降低活性,因为要确保所有节点都达成一致可能需要更多的通信和确认。相反,过于追求活性可能会降低安全性,因为为了更快地做出决策,可能会放宽对于恶意行为的检测和处理。共识算法的设计和分析要在安全性和活性之间取得平衡,以满足特定的应用需求和性能要求。