分布式事务 - 理论模型
提到事务,很多人第一时间想到的是数据库的事务。所谓的数据库事务是指作为单个逻辑工作单元执行的多个数据库操作,要么同时成功,要么同时失败,它必须满足ACID特性,即:
原子性(Atomicity):事务必须是原子工作单元,不可继续分割,要么全部成功,要么全部失败。
一致性(Consistency):事务完成时,所有的数据必须都保持一致。
隔离性(Isolation):由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。
持久性(Durability):事务执行完成之后,它对于系统的影响是永久性的。
上述是针对单库多表的情况下事务所需要满足的特性。在微服务架构下,随着业务服务的拆分以及数据库的拆分,会存在下图所示的场景,订单和库存分别拆分成了两个独立的数据库,当客户端发起一个下单操作时,需要在订单服务对应的数据库中创建订单,同时需要基于RPC通信调用库存服务完成商品库存的扣减。
在这样一个场景中,原本的单库事务操作就变成了多个数据库的事务操作,由于每个数据库的事务执行情况只有自己知道,比如订单数据库并不知道库存数据库的执行结果,这样就会导致订单数据库和库存数据库的数据不一致的问题,比如订单创建成功,库存扣减失败,就可能导致"超卖"问题。
这就是所谓的分布式事务场景,准确来说,分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于分布式系统的不同节点上。
分布式事务的理论模型
分布式事务问题也叫分布式数据一致性问题,简单来说就是如何在分布式场景中保证多个节点数据的一致性。分布式事务产生的核心原因在于存储资源的分布性,比如多个数据库,或者MySQL和Redis两种不同存储设备的数据一致性等。在实际应用中,我们应该尽可能地从设计层面避免分布式事务的问题,因为任何一种解决方案都会增加系统的复杂度。下面我们来了解一下分布式事务问题的常见解决方案。
X/Open分布式事务模型
X/Open DTP(X/Open Distributed Transaction Processing Reference Model)是X/Open这个组织定义的一套分布式事务的标准。这个标准提出了使用两阶段提交(2PC)来保证分布式事务的完整性。如下图所示,X/Open DTP中包含三种角色:
AP:Application,表示应用程序。
RM:Resource Manager,表示资源管理器,比如数据库。
TM:Transaction Manager,表示事务管理器,一般指事务协调者,负责协调和管理事务,提供AP编程接口或管理RM。可以理解为Spring中提供的Transaction Manager。
上图所展示的角色和关系与本地事务的原理基本相同,唯一不同的在于,如果此时RM代表数据库,那么TM需要能够管理多个数据库的事务,大致实现步骤如下:
配置TM,把多个RM注册到TM,相当于TM注册RM作为数据源。
AP从TM管理的RM中获得连接,如果RM是数据库则获取JDBC连接。
AP向TM发起一个全局事务,生成全局事务ID(XID),XID会通知各个RM。
AP通过第二步获得的连接直接操作RM完成数据操作,这时,AP在每次操作时会把XID传递给RM。
AP结束全局事务,TM会通知各个RM全局事务结束。
根据各个RM的事务执行结果,执行提交或者回滚操作。
为了更清晰地理解,可以参考下图所示的流程图,实际上这里会设计全局事务的概念,也就是说,在原本的单机事务下,会存在跨库事务的可见性问题,导致无法实现多节点事务的全局可控。而TM就是一个全局事务管理器,它可以管理多个资源管理器的事务。TM最终会根据各个分支事务的执行结果进行提交或者回滚,如果注册的所有分支事务中任何一个节点事务执行失败,为了保证数据的一致性,TM会触发各个RM的事务回滚操作。
TM和多个RM之间的事务控制,是基于XA协议来完成的。XA协议是X/Open提出的分布式事务处理规范,也是分布式事务处理的工业标准,它定义了xa_和ax_系列的函数原型及功能、约定等。目前Oracle、MySQL、DB2等都实现了XA接口,所以他们都可以作为RM。
两阶段提交协议
上图中TM实现了多个RM事务的管理,实际上会设计两个阶段的提交,第一阶段是事务的准备阶段,第二阶段是事务的提交或者回滚阶段。这两个阶段都是由事务管理器发起的。两阶段提交协议的执行流程如下:
准备阶段:事务管理器(TM)通知资源管理器(RM)准备分支事务,记录事务日志,并告知事务管理器准备的结果。
提交/回滚阶段:如果所有的资源管理器(RM)在准备阶段都明确返回成功,则事务管理器(TM)向所有的资源管理器(RM)发起事务提交指令完成数据的变更。反之,如果任何一个资源管理器(RM)明确返回失败,则事务管理器(TM)会向所有资源管理器(RM)发送事务回滚指令。完整的执行流程如下图所示:
两阶段提交将一个事务的处理过程分为投票和执行两个阶段,它的优点在于充分考虑到了分布式系统的不可靠因素,并且采用非常简单的方式(两阶段提交)就把由于系统不可靠而导致事务提交失败的概率降到最小。当然,它也有以下缺点:
同步阻塞:从上图的执行流程来看,所有参与者(RM)都是事务阻塞型的,对于任何一次指令都必须要有明确的响应才能继续进行下一步,否则会处于阻塞状态,占用的资源一直被锁定。
过于保守:任何一个节点失败都会导致数据回滚。
事务协调者的单点故障:如果协调者在第二阶段出现了故障,那么其他的参与者(RM)会一直处于锁定状态。
脑裂导致数据不一致的问题:在第二阶段中,事务协调者向所有参与者(RM)发送commit请求后,发生局部网络异常导致只有一部分参与者(RM)接收到了commit请求,这部分参与者收到请求后会执行commit操作,但是未收到commit请求的节点由于事务无法提交,导致数据出现不一致问题。
三阶段提交协议
三阶段提交协议是两阶段提交协议的改进版本,它利用超时机制解决了同步阻塞的问题,三阶段提交协议的具体描述如下:
CanCommit(询问阶段):事务协调者向参与者发送事务执行请求,询问是否可以完成指令,参与者只需回答是或者不是即可,不需要做真正的事务操作,这个阶段会有超时中止机制。
PreCommit(准备阶段):事务协调者会根据参与者的反馈结果决定是否继续执行,如果在询问阶段所有参与者都返回可以执行操作,则事务协调者会向所有参与者发送PreCommit请求,参与者收到请求后写redo和undo日志,执行事务操作但是不提交事务,然后返回ACK响应等待事务协调者的下一步通知。如果在询问阶段任意参与者返回不能被执行操作的结果,那么事务协调者会向所有参与者发送事务中断请求。
DoCommit(提交或回滚阶段):这个阶段也会存在两种结果,仍然根据上一步骤的执行结果来决定DoCommit的执行方式,如果每个参与者在PreCommit阶段都返回成功,那么事务协调者会向所有参与者发起事务提交指令。反之,如果参与者中的任一参与者返回失败,那么事务协调者就会发起中止指令来回滚事务。
三阶段提交协议和两阶段提交协议相比有一些不同点:
增加一个CanCommit阶段,用于询问所有参与者是否可以执行事务操作并响应,它的好处是可以尽早发现无法执行操作而中止后续的行为。
在准备阶段之后,事务协调者和参与者都引入了超时机制,一旦超时,事务协调者和参与者会继续提交事务,并且认为处于成功状态,因为在这种情况下事务默认为成功的可能性比较大。
实际上,一旦超时,在三阶段提交协议下仍然可能出现数据不一致的情况,当然概率是比较小的。另外,最大的好处就是基于超时机制来避免资源的永久锁定。
不管是两阶段还是三阶段协议,都是数据一致性解决方案的实现,可以在实际应用中灵活的调整。
CAP定理和BASE理论
上面提到的两阶段提交和三阶段提交是XA协议解决分布式数据一致性问题的基本原理,但是这两种方案为了保持数据的强一致性,降低了可用性。实际上这里涉及分布式事务的两个理论模型。
CAP定理
简单来说它是指在分布式系统中不可能同时满足一致性(C: Consistency)、可用性(A: Availability)、分区容错性(P:Partition Tolerance)这三个基本需求,最多同时满足两个。
C:数据在多个副本中要保持强一致,比如前面说的分布式数据一致性问题。
A:系统对外提供的服务必须一直处于可用状态,在任何故障下,客户端都能在合理的时间内获得服务端的非错误响应。
P:在分布式系统中遇到任何网络分区故障,系统仍然能够正常对外提供服务。
CAP定理证明,在分布式系统中,要么满足CP、要么满足AP,不可能实现CAP或者CA。原因是网络通信并不是绝对可靠的,比如网络延时、网络异常等都会导致系统故障。而在分布式系统中,即便出现网络故障也需要保证系统仍然能够正常对外提供服务,所以在分布式系统中Partition Tolerance是必然存在的,也就是需要满足分区容错性。
如果是CA或者CAP这种情况,相当于网络百分之百可靠,否则当出现网络分区的情况时,为了保持数据的一致性,必须拒绝客户端的请求。但是如果拒绝了请求,就无法满足A,所以在分布式系统中不可能选择CA,因此只能有AP或者CP这两种选择。
AP:对于AP来说,相当于放弃了强一致性,实现最终的一致,这是很多互联网公司解决分布式数据一致性问题的主要选择。
CP:放弃了高可用性,实现强一致性。前面提到的两阶段提交和三阶段提交都采用这种方案。可能导致的问题是用户完成一个操作会等待较长的时间。
BASE理论
BASE理论是由于CAP中一致性和可用性不可兼得而衍生出来的一种新的思想,BASE理论的核心思想是通过牺牲数据的强一致性来获得高可用性。它有如下三个特征:
Basically Available(基本可用):分布式系统在出现故障时,允许损失一部分功能的可用性,保证核心功能的可用。
Soft State(软状态):允许系统中的数据存在中间状态,这个状态不影响系统的可用性,也就是允许系统中不同节点的数据副本之间的同步存在延时。
Eventually Consistent(最终一致性):中间状态的数据在经过一段时间之后,会达到一个最终的数据一致性。
BASE理论并没有要求数据的强一致性,而是允许数据在一段时间内是不一致的,但是数据最终会在某个时间点实现一致。在互联网产品中,大部分都会采用BASE理论来实现数据的一致,因为产品的可用性对于用户来说更加重要。
举个例子,在电商平台中用户发起一个订单的支付,不需要同步等待支付的执行结果,系统会返回一个支付处理中的状态到用户界面。对于用户来说,他可以从订单列表中看到支付的处理结果,而对于系统而言,当第三方支付处理成功之后,再更新该订单的支付状态即可。在这个场景中,虽然订单的支付状态和第三方的支付状态存在短期的不一致,但是用户却获得了更好的产品体验。