一、数据库事务的ACID特性
数据库事务特性包括原子性(Atomicity )、一致性( Consistency )、隔离性或独立性( Isolation)和持久性(Durabilily)。以下单为例,在单体的电商系统中,调用下单服务,整个服务操作在同一事务中完成。下单成功以后,扣减库存,生成订单操作会同时生效,数据都会写入库存数据库,订单数据库。流程如下:
在分布式系统中,如果只是将下单服务进行微服务化,即将下单服务拆分为调用订单服务API和商品服务API。微服务拆分后,订单服务API对应独立的订单数据库,商品服务API对应独立的商品数据库。如果,直接按照单体服务中的的流程,只是将原来的本地方法替换微服务API的话,其流程图如下:
在下单服务中,使用RPC或者HTTP请求调用商品服务API和订单服务API,类似client(客户端)调用server(服务器)方式。此时,如果出现如下两个问题:
- 网络异常,client调用server服务调用成功,但是因为网络异常,client显示调用失败。
- 服务器宕机,client调用server服务调用成功,但是server服务器宕机了,client显示调用失败。
流程图如下:
因为,如上两个原因,会造成商品与订单数据的数据状态不一致,情况如下:
- 订单服务因为网络异常或者服务器宕机,订单数据回滚在订单数据库中没有生成数据,而商品服务调用成功,商品数据库中的库存扣减成功。
- 订单服务调用成功,生成的订单数据写入了订单数据库,而商品服务因为网络异常或者服务器宕机而调用失败,商品数据库中的库存没有变化。
如何解决直接拆分为微服务后,数据状态不一致的问题呢?这时,就需要引入分布式事务了。
二、两阶段提交(2PC)协议
原理详解
首先,会想到通过数据库的两阶段提交(2PC)方式实现分布式事务。根据CAP理论(详细可以参考深入理解CAP理论和适用场景),2PC属于CP模式。该协议分为以下两个阶段:
- 第一阶段(提交事务请求):事务协调者向所有的事务参与者发送提交事务的请求,根据所有参与者返回的是否可以提交的标识,决定下一步执行操作。如果有一个参与者返回不可提交的标识,则所有事务参与者事务回滚。
- 第二阶段(执行事务提交):在第一阶段中,所有事务参与者允许事务提交后,事务协调者向所有事务参与者发送提交事务请求,所有事务参与者提交事务,并返回ack确认消息给事务协调者。
两阶段提交流程正常的执行流程,如下:
这种方式操作简单,通俗易懂,该中方式也有如下几个缺点:
- 同步阻塞问题,极大的限制性了分布式系统性能。当在执行第二阶段提交时,所有事务参与者都会阻塞,等待其他事务参与者的执行,自己将无法执行任何操作。当两个阶段中,某个事务参与者执行比较耗时,但是最终成功执行,这时,其他参与者会因为等待该的返回状态而阻塞等待,而影响了整个过程的效率。流程如下:
- 单点故障,两个阶段都是由协调者参与才能完成,一旦协调者发生故障,参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作,操作流程如下:
- 数据不一致,在二阶段提交的过程中,当事务协调者向部分参与者成功发送事务请求之后。如果发生以下两种情况,
- 事务协调者因为网络异常无法送法请求,或者服务器宕机。
- 某个事务参与者在收到事务提交请求后服务器宕机。
以上,两种情况会导致部分数据提交成功,部分提交失败,造成数据不一致。操作流程如图:
- 没有完善的容错机制, 在两阶段操作过程中事务协调者向事务参与者发送预提交请求或者提交事务请求时,如果,参与者出现了异常而导致事务协调者无法获取所有事务参与者的响应信息,这时,只能依靠事务协调者的超时机制来判断下一步操作。此外,任何一个事务参与者的失败都会造成整个事务流程的失败。操作流程如下:
实现方案
TX-LCN分布式事务框架(LCN事务模式),基于2PC协议实现的分布式事务解决方案。TX-LCN由TxClient事务发起方、TxManager事务协调者组成。执行步骤如下:
-
创建事务组,事务发起方开始执行业务代码之前先调用TxManager创建事务组对象,然后拿到事务标示GroupId的过程。
-
加入事务组,事务参与方在执行完业务方法以后,将该模块的事务信息通知给TxManager的操作。
-
通知事务组,事务发起方执行完业务代码以后,将发起方执行结果状态通知给TxManager,TxManager将根据事务最终状态和事务组的信息来通知相应的参与模块提交或回滚事务,并返回结果给事务发起方。
如上的操作过程如图:
优劣势分析
优势:TxManager作为分布式服务,实现了事务协调者的角色,如果集群部署,避免了事务协调者单点故障的问题。
劣势:事务参与者的同步阻塞问题,长事务,数据不一致没有解决。
TCC模式,该种模式也类似与两阶段提交方式,总共由三个步骤,分成 Try,Confirm / Cancel 两个阶段,try阶段执行成功,则执行Confirm阶段,反之则执行Cancel阶段。三个阶段的操作内容如下:
- Try 阶段主要是对业务系统做检测及资源预留。
- Confirm 阶段主要是对业务系统做确认提交,Try阶段执行成功后就执行Confirm阶段。
- Cancel 阶段主要是在业务执行错误,在Try阶段执行失败后,用于执行Try 阶段执行的业务取消,预留资源释放。
操作流程如下:
TCC模式的实现具体方案包括:
- tcc-transaction(具体的代码实例可以参考tcc-transaction-使用指南1.2.x)
- 阿里巴巴开源的分布式事务中间件seata(seata(包含TCC实现方式))
- TX-LCN分布式事务框架-TCC实现方案(tx-lcn-TCC方案)
优劣势分析
优势:三个阶段的事务单独控制,避免了2PC中所有事务参与者同步阻塞的问题。对于三个阶段实现容错机制,如果在三个阶段中执行发生异常(网络超时,乐观锁异常等等),每个阶段都会对执行的操作进行重试,所以,TCC三个阶段逻辑要保证幂等性。TCC模式也属于数据强一致模式,适用于金融行业对数据一致性要求比较高的情景。
劣势:该模式对代码的嵌入性高,要求每个业务需要写三种步骤的操作,并且,数据一致性控制几乎完全由开发者控制,对业务开发难度要求高。在confirm和cancel阶段,如果重试以后还是抛出异常,则会造成数据不一致。
三、BASE理论
原理详解
在分布式系统中,一般更加重视系统的可用性,分布式事务解决方案都基于BASE理论,它是用来对CAP定理进行进一步扩充的。BASE理论指的是:
- Basically Available(基本可用)
- Soft state(软状态)
- Eventually consistent(最终一致性)
BASE理论是对CAP中的一致性和可用性进行一个权衡的结果,理论的核心思想就是:我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。
实现方案
可靠消息(事物消息)中间件+软状态+业务消息回查,实现的思路如下:
- 上游服务执行成功以后,业务数据处于中间状态(订单状态支付中,用户取车中等等),然后,发送可靠消息(事物消息)。
- 下游服务消费消息,保证业务幂等,并且回查一下业务状态保证最终一致性。
优劣势分析
优势:提高并发,对于类似于12306下单和支付分离的场景,在下单时,几乎只同步操作下单的操作。
劣势:如果消息消费失败,容易出现数据不一致的情况,使用消息补偿机制会延时保障数据最终一致。