随着计算机技术的不断进步和相关应用规模的迅速扩大,传统的集中式系统已逐渐被分布式系统所取代(集中式的系统架构存在诸多单点问题),以适应大型互联网应用的需求。分布式系统具有以下特点:多节点分布、对等性、并发性、全局时钟难以定义以及故障的不可避免性。
在软件开发中,我们经常需要处理事务,事务具有原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)的特点。在单机数据库中,实现这些特性相对容易,但是在分布式系统中,数据分散在不同的机器上,实现分布式事务会面临着许多挑战。 CAP理论于2000年7月被正式提出,指出一个分布式系统不可能同时满足一致性、可用性和分区容错性这三个基本需求,最多只能同时满足其中的两项。通常情况下需要牺牲一致性或者可用性,可以类比为购房时不可能同时具备三个条件。 具体来说,CAP理论中的三个要素包括:
CAP定理
BASE理论是Basically Available(基本可用)、Soft state(软状态)和Eventually Consistent(最终一致性)三个的集合。BASE是对一致性和可用性权衡的结果,其核心思想是即使无法做到强一致性,但每个应用可以根据自身业务的特点,采用适当的方式使系统达到最终一致性。
在分布式系统中,各个节点在物理上相互独立,通过网络进行通信和协调。尽管每个独立节点都有事务机制,可以保证ACID属性,但因为分布式事务涉及在多个数据库间执行操作,将单一数据库事务概念扩展到多个数据库,节点之间无法准确了解其他节点的事务执行情况。因此,理论上在没有第三方参与的情况下,两台机器无法达到一致状态。 若要保证分布式部署的多台机器中数据的一致性,需确保所有节点的数据写操作要么全部执行,要么全部不执行。然而,在执行本地事务时,一台机器无法知道其他机器中本地事务的执行结果,因此无法确定事务应提交还是回滚。分布式事务处理的关键在于需要一种方法追踪事务在各处的所有动作,确保提交或回滚决策产生统一结果(全部提交或全部回滚)。因此,常规解决方案是引入一个“协调者”组件,以统一调度所有分布式节点的执行。
在长期的研究中,为了解决分布式系统一致性问题,出现了一些经典的一致性协议和算法。接下来将分别介绍它们各自的优缺点。
二阶段提交协议(Two-phase Commit,即2PC)是一个经典的强一致性、中心化的原子提交协议。它将事务的提交过程分成了两个阶段来进行处理,分别是表决阶段(Propose)和提交阶段(commit)。
在第一阶段(提交请求阶段),协调者节点向所有参与者节点询问是否可以执行提交操作,并开始等待各参与者节点的响应。参与者节点执行询问发起为止的所有事务操作,并将Undo信息和Redo信息写入日志。然后,各参与者节点会响应协调者节点发起的询问。如果参与者节点的事务操作实际执行成功,则它返回一个"同意"消息;如果参与者节点的事务操作实际执行失败,则它返回一个"中止"消息。
如果协调者节点从所有参与者节点获得的响应消息都为"同意",则会向所有参与者节点发出"正式提交"的请求。接着,参与者节点会正式完成操作,并释放在整个事务期间内占用的资源,然后向协调者节点发送"完成"消息。一旦协调者节点收到所有参与者节点反馈的"完成"消息,就会完成事务。 相反,如果任一参与者节点在第一阶段返回的响应消息为"终止",或者协调者节点在第一阶段的询问超时之前无法获取所有参与者节点的响应消息,协调者节点会向所有参与者节点发出"回滚操作"的请求,参与者节点利用之前写入的Undo信息执行回滚,并释放在整个事务期间内占用的资源,参与者节点向协调者节点发送"回滚完成"消息。协调者节点收到所有参与者节点反馈的"回滚完成"消息后,取消事务。 事务提交 事务回滚
二阶段提交原理简单、实现方便,但缺点也很明显,如下图。
三阶段提交协议主要是为了解决2PC协议的阻塞问题。与两阶段提交不同的是,三阶段提交有两个改动点。
3PC协议分为CanCommit、PreCommit、DoCommit三个阶段。
执行事务
中断事务
提交事务
中断事务
与2PC相比,3PC降低了阻塞范围,并且在等待超时后,协调者或参与者会中断中断事务,避免协调者单点问题,阶段三中协调者出现问题时,参与者会继续提交事务。
数据不一致的问题依旧存在,当在参与者收到preCommit请求后等待doCommit指令时,此时如果协调者请求中断事务,而协调者因为网络问题无法与参与者正常通信,会导致参与者继续提交事务,造成数据不一致。
2PC 和 3PC 在一定程度上是可以解决数据一致性问题的。 但是并没有完全解决就是协调者宕机的情况。如何解决2PC和3PC的存在的问题呢? 引入多个协调者,又引入主协调者,这个就是最简单的一种Paxos 算法.
Leslie Lamport在1990年提出了Paxos算法,这是工程实践中少数被证实的强一致性、高可用、去中心化的分布式协议之一,它也是基于消息传递且具有高度容错特性的一致性算法,它在有限时间内在多个副本之间达成共识,是目前公认的解决分布式一致性问题最有效的算法之一。它允许消息重复、丢失、延迟或乱序,但没有拜占庭式错误的网络环境中,它利用「大多数 (Majority)机制」保证了2F+1的容错能力,即2F+1个节点的系统最多允许F个节点同时出现故障。
拜占庭式错误释义:一般地把出现故障但不会伪造信息的情况称为“非拜占庭错误”(Non-Byzantine Fault)或“故障错误”(Crash Fault);而伪造信息恶意响应的情况称为“拜占庭错误”(Byzantine Fault)。
解决了什么问题 在常见的分布式系统中,总会发生诸如机器宕机或网络异常(包括消息的延迟、丢失、重复、乱序,还有网络分区)等情况。Paxos算法需要解决的问题就是如何在一个可能发生上述异常的分布式系统中,快速且正确地在集群内部对某个数据的值达成一致,并且保证不论发生以上任何异常,都不会破坏整个系统的一致性。
Paxos 使用两阶段提交(prepare 和 accept)来对某一个值(在不同的场景中是不同的含义,可以是一条日志或者是命令)达成一致,它把节点分为proposer、acceptor和learner三种角色。包括以下三种
Paxos的版本有Basic Paxos
、Multi Paxos
、Fast-Paxos
, 具体落地有Raft
和zk的ZAB
协议。
算法分为两个阶段:Prepare阶段和Accept阶段。
假设分布式环境中有两个Proposer和三个Acceptor,ProposerB成功发送prepare请求,在发送Accept请求时出现故障宕机,只成功给Acceptor1发送了accept请求并得到响应。
ProposerA发起prepare(3)的请求,最终选择提案编号3作为当前的提案值。
Acceptor 每次同意新的提案值都会将消息同步给 Learner,Learner 根据各个 Acceptor 的反馈判断当前是否已超过半数同意。如果达成共识则发送广播消息给所有 Acceptor 和 Proposer 并结束提案。 活锁 当一个proposer提交的Accept请求被拒绝时,可能是因为acceptor已经承诺了只接受更大编号的Prepare请求,因此proposer提高编号继续提交。如果多个proposer都发现自己的编号过低转而提出更高编号的proposal,会导致死循环,也称为活锁。Paxos的作者本人提出的方案是leader选举,即从Proposer中选举一个leader,只能由leader提交提案,当Leader宕机时马上再选举其他的Leader。
basic paxos是对一个值达成一致,可以将每一次basic paxos 的流程称之为 paxos instance。如果想对一系列的value 达成一致,那么就执行一系列的 paxos instance,但是这样效率是非常低的。
#为了在Multi-Paxos中解决先前存在的问题,引入了leader的角色,仅有leader才能提出提案并直接进入accept阶段。相较之下,在Basic-Paxos中是通过Proposal Number实现的,每个日志对应一个独立的执行过程,不同日志的Proposal Number是隔离的,因此无法确定不同日志之间的顺序。Multi-Paxos中的Proposer-Leader的引入使得可以为所有日志设置全局序号,通过全局序号来确定不同日志的顺序,从而解决了这一问题。这使得Multi-Paxos具备了对全局日志整体的感知能力。 目前市场上实际的开源项目,大部分也并不是采用了 Paxos 或者 Multi-Paxos 算法,而是往往采取了简化和变形,几个使用paxos协议的系统见下表。
系统名称 | 简单介绍 |
---|---|
Chubby | 一个粗粒度的锁服务,主要解决 GFS、Bigtable 这些系统的元数据的一致性问题,以及容错场景下的灾难恢复问题。 |
Megastore | 支持SQL的跨数据中心的分布式数据库,只是google迈向spanner的一个过渡产品。号称多数据中心的同步,但是是有前提的,得在同一个分区即实体组,只能保证实体组内的事务 |
Spanner | 采用了原子钟 +GPS 时钟,可以把时钟误差范围缩小到 1 毫秒到 7 毫秒之间,来更好地实现分布式事务下的可线性化。 |
OceanBase | 阿里自研的原生分布式关系数据库 |
Paxos 算法可以从任何一个节点发起数据写入,达成一致的流程也比较繁琐,难以理解。而 Raft 算法则把问题拆分成了一个个独立的子问题,比如 Leader 选举(Leader Election)、日志复制(Log Replication)、安全(Safety)和成员变化(Membership Changes)等等。现在市面上有很多对Raft的实现,比如Etcd、Consul等等。
Raft的集群角色分成这3中,不同的节点在运行环境中处于不同的角色,任何节点的任何一个时刻都处于以下三种角色之一。
raft节点角色流转图 Raft 算法是有一个强 Leader 的,而 Paxos 算法是无 Leader 或者弱 Leader 的。而因为有了强 Leader,Raft 算法就可以进一步地要求日志里面不能有空洞,几个明细的问题解决方案如下:
本文作者:唐文杰
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!