本文我们介绍一下分布式事务、二阶段提交和三阶段提交。

1. 分布式数据一致性

在分布式系统中,为了保证数据的高可用,通常会将数据保留多个副本(replica),这些副本会放置在不同的物理机器上。

1) 什么是数据一致性

在数据有多个副本的情况下,如果网络、服务器或者软件出现故障,会导致部分副本写入成功,部分副本写入失败。这就造成了各个副本之间的数据不一致,数据内容冲突。造成事实上的数据不一致。

2) CAP定理

CAP理论认为在分布式的环境下设计和部署系统时,有3个核心的需求:ConsistencyAvailabilityPartition Tolerance,即CAP。下面介绍一下CAP的这三个要求:

  • 一致性(C): 在分布式系统中的所有数据备份,在同一时刻是否有同样的值。(等同于所有节点访问同一份最新的数据副本);

  • 可用性(A): 在集群中的一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)

  • 分区容错性(P): 分区容忍性,是否可以对数据进行分区。以实际效果而言,分区相当于对通信的时限要求。系统如果不能在规定时限内达成数据一致性,就意味着发生了分区,必须就当前操作在C和A之间做出选择。

3) 数据一致性模型

一些分布式系统通过复制数据来提高系统的可靠性和容错性,并且将数据的不同副本存放在不同的机器上。

  • 强一致性: 当更新操作完成以后,任何多个后续进程或线程的访问都会返回最新的更新过的值。这种是对用户最友好的,就是用户上一次写什么,下一次就保证能读到什么。根据CAP理论,这种实现需要牺牲可用性。

  • 弱一致性: 系统并不能保证后续进程或线程的访问都会返回最新的更新过的值。用户读到某一操作对系统特定数据的更新需要一段时间,我们称这段时间为不一致性窗口。系统在数据写入成功之后,不承诺立即可以读到最新写入的值,也不会具体的承诺多久之后可以读到。

  • 最终一致性: 是弱一致性的一种特例。系统保证在没有后续更新的前提下,系统最终返回上一次更新操作的值。在没有故障发生的前提下,不一致窗口的时间主要受通信延迟、系统负载和复制副本的个数的影响。DNS是一个典型的最终一致性系统。

2. 典型的分布式事务实例

跨行转账问题是一个典型的分布式事务,用户A向用户B转账1000元,要进行A的余额-1000,B的余额+1000,显然必须保证这两个操作的事务性。类似的还有,电商系统中,当用用户下单后,除了在订单表插入记录,还要在商品表更新库存等,特别是随着微服务架构的流行,分布式事务的场景变得更普遍。

3. 两阶段提交协议

两阶段提交协议是协调所有分布式原子事务参与者,并决定提交或取消(回滚)的分布式算法。

1) 协议参与者

在两阶段提交协议中,系统一般包含两类机器(或节点): 一类为协调者(coordinator),通常一个系统中只有一个; 另一类为事务参与者(participants, cohorts或workers),一般包含多个,在数据库存储系统中可以理解为数据副本的个数。协议中假设每个节点都会记录写前日志(write-ahead log)并持久性存储,即使节点发生故障日志也不会丢失。协议中同时假设节点不会发生永久性故障而且任意两个节点都可以相互通信。

two-phase-commit

2) 两阶段的执行

2.1) 请求阶段(commit-request phase, 或称表决阶段,voting phase): 在请求阶段,协调者将通知事务参与者准备提交或取消事务,然后进入表决过程。在表决过程中,参与者将告知协调者自己的决策————同意(事务参与者本地作业执行成功)或取消(本地作业执行故障)

2.2) 提交阶段(commit phase): 在该阶段,协调者将基于第一个阶段的投票结果进行决策————提交或取消。当且仅当所有的参与者同意提交事务,协调者才通知所有的参与者提交事务; 否则协调者将通知所有的参与者取消事务。参与者在接收到协调者发来的消息后将执行相应的操作。

3) 两阶段提交的缺点

3.1) 同步阻塞问题。执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源时不得不处于阻塞状态。

3.2) 单点故障。由于协调者的重要性,一旦协调者发生故障,参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。(如果协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题)

3.3) 数据不一致。在二阶段提交的阶段二中,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中协调者发生了故障,这会到这只有一部分参与者接受到了commit请求。而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据不一致性的现象。

4)两阶段提交无法解决的问题

当协调者出错,同时参与者也出错时,两阶段提交无法保证事务执行的完整性。

考虑协调者在发出commit消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否已经提交。

4. 三阶段提交协议

三阶段提交协议在协调者和参与者中间都引入超时机制,并且把两阶段提交协议的第一个阶段拆分成了两步: 询问,然后再锁资源,最后才真正提交

three-phase-commit

4.1 三个阶段的执行

1) CanCommit阶段

3PC的CanCommit阶段其实和2PC的准备阶段很像。协调者向参与者发送是否可以Commit的请求,参与者如果可以提交就返回Yes响应,否则返回No响应。

2) PreCommit阶段

协调者(Coordinator)根据参与者(Cohort)的反应情况来决定是否可以继续事务的PreCommit操作。根据响应情况,有以下两种可能:

2.1) 假如Coordinator从所有的Cohort获得的反馈都是Yes响应,那么就会进行事务的预执行: 发送预提交请求。Coordinator向Cohort发送PreCommit请求,并进入Prepared阶段。Cohort接收到PreCommit请求后,会执行事务操作,并将undo和redo信息记录到事务日志中,然后响应反馈。如果Cohort成功的执行了事务操作,则返回ACK响应,同时开始等待最终指令。

2.2) 假如有任何一个Cohort向Coordinator发送了No响应,或者等待超时之后,Coordinator都没有接到Cohort的响应,那么就中断事务:发送中断请求。Coordinator向所有Cohort发送abort请求来中断事务,Cohort收到来自Coordinator的abort请求之后(或超时之后,仍未收到Cohort的请求),执行事务的中断。

3) DoCommit阶段

该阶段进行真正的事务提交,也可以分为以下两种情况:

3.1) 执行提交

A. 发送提交请求。 Coordinator接收到Cohort发送的ACK响应,那么他将从预提交状态进入到
提交状态。并向所有Cohort发送DoCommit请求。

B. 事务提交。Cohort接收到DoCommit请求之后,执行正式的事务提交。并在完成事务提交之后
释放所有事务资源。

C. 响应反馈。 事务提交完之后,向Coordinator发送ACK响应

D. 完成事务。Coordinator接收到所有Cohort的ACK响应之后,完成事务。

3.2) 中断事务

Coordinator没有接收到Cohort发送的ACK响应(可能是接受者发送的不是ACK响应,也可能响应超时),那么就会执行中断事务。

4.2 三阶段提交协议和两阶段提交协议的不同

对于协调者(Coordinator)和参与者(Cohort)都设置了超时机制(在2PC中,只有协调者拥有超时机制,即如果在一定时间内没有收到cohort的消息则默认失败)。

在2PC的准备阶段和提交阶段之间,插入预提交阶段,使3PC拥有CanCommit、PreCommit、DoCommit三个阶段。PreCommit是一个缓冲,保证了在最后提交之前各参与节点的状态是一致的。

如下只摘自维基百科:

三阶段提交是非阻塞协议。三阶段提交在两阶段提交的第一个阶段和第二个阶段之间插入了一个准备阶段,使得原先在两阶段提交中,参与者在投票之后,由于协调者发生崩溃或错误,而导致参与者处于无法知晓是否提交中止“不确定状态”所产生的可能相当长的延时的问题得以解决。

举例来说,假设有一个决策小组由一个主持人和多位组员以电话联络的方式协调是否通过一个提案,以两阶段提交来说,主持人收到一个提案请求,打电话跟每个组员询问是否通过并统计回复,然后将最后决定打电话通知各组员。要是主持人在跟第一位组员通完电话后失忆,而第一位组员在得知结果并执行后老人痴呆,那么即使重新选出主持人,也没人知道最后的提案决定是什么,也许是通过,也许是驳回,不管大家选择哪一种决定,都有可能与第一位组员已执行过的真实决定不一致,老板就会不开心认为决策小组沟通有问题而解雇。

三阶段提交即是引入了另一个步骤,主持人打电话跟组员通知请准备通过提案,以避免没人知道真实决定而造成决定不一致的失业危机。

为什么能够解决二阶段提交的问题呢? 回到刚刚提到的状况,在主持人通知完第一位组员请准备通过后两人意外失忆,即使没人知道全体在第一阶段的决定为何,全体决策组员仍可以重新协调过程或直接否决,不会有不一致决定而失业。那么当主持人通知完全体组员请准备通过并得到大家的再次确认后进入第三阶段,当主持人通知第一位组员请通过提案后两人意外失忆,这时候其他组员再重新选出主持人后,仍可以知道目前至少是处于准备通过提案阶段,表示第一阶段大家都已经决定要通过了,此时便可以直接通过。

4.3 三阶段提交协议的缺点

如果进入PreCommit后,Coordinator发出的是abort请求,假设只有一个Cohort受到并进行了abort操作,而其他对于系统状态位置的Cohort会根据3PC选择继续Commit,此时系统状态发生不一致性。



[参看]:

  1. 对分布式事务及两阶段提交、三阶段提交的理解