vlambda博客
学习文章列表

6种最常见分布式事务解决方案!


一、分布式事务



分布式事务是指事务的参与者,支持事务的服务器,资源服务器和事务管理器位于不同分布式系统的不同节点上。以上是百度百科的解释。简而言之,大型操作由不同的小型操作组成。这些小操作分布在不同的服务器上,并且属于不同的应用程序。分布式事务需要确保这些小操作要么全部成功,要么全部失败。本质上,分布式事务是为了确保不同数据库中的数据一致性。


以商品流水账单为例,我们拆分为商品购买系统,订单系统,支付系统。


用户看中产品后,点击购买。
响应于用户的点击,商品购买系统将一条订单信息插入到订单系统中。
跳至付款系统以完成付款。
在用户购买商品的整个过程中,我们需要确保事件1、2和3均成功执行,没有例外。一旦系统引发异常,就需要将其回滚。

那么,如何确保每个子系统的操作是一致的呢?这是我们下面提到的分布式事务解决方案。

在这里,本文没有提及分布式共识协议,以下是简要列表,有兴趣的读者可以参考其他详细信息:

两阶段提交协议
三相提交协议
Paxos协议
Raft协议


二、分布式事务的解决方案



1.两阶段提交方案/XA方案

该方案基于两阶段提交协议,因此也称为两阶段提交方案。在此分布式系统中,一个系统需要充当协调者,而其他系统则充当参与者。主要分为提交请求阶段和提交阶段

请求阶段:首先,协调员将向所有参与者发送请求提交或取消提交的请求,然后收集参与者的决定。
提交阶段:协调器将收集所有参与者的决策信息,并且仅当所有参与者向确认者发送确认消息时,协调器才会提交请求,否则它将执行回滚或取消请求。


该方案的缺点:

同步阻止:所有参与者都是事务同步阻止类型。当参与者占用公共资源时,必须阻止其他第三方节点访问公共资源。
单点故障:协调器发生故障后,系统将不可用。
数据不一致:协调器发送提交后,一些参与者收到提交消息,事务成功执行,有些未收到,并且处于阻塞状态。在此时间段内将发生数据不一致。
不确定性:协调器发送提交后,只有一个参与者接收提交在这个时候,当参与者和协调员下降的同时,重新选举协调员无法确定消息是否已成功提交。
XA方案的实现可以使用Spring + JTA来实现,您可以参考以下文章:Springboot + atomikos + jta实现了分布式事务的统一管理


2.TCC方案

TCC方案分为Try Confirm Cancel三个阶段,属于补偿性分布式事务。


TCC方案分为三个阶段:“尝试确认取消”,这是一种补偿性分布式事务。

尝试:尝试执行业务
此过程不执行业务,而是完成所有业务的一致性检查,并保留执行所需的所有资源
确认:开展业务
这个过程确实开始执行业务。由于一致性检查已在“尝试”阶段完成,因此无需任何检查即可直接执行此过程。在执行过程中,将使用在“尝试”阶段保留的业务资源。
取消:取消已执行的业务
如果业务执行失败,它将进入“取消”阶段,这将释放所有占用的业务资源并回退在“确认”阶段执行的操作。
以电子商务系统用户的购买渠道为例。


Try阶段:

6种最常见分布式事务解决方案!

在“尝试”阶段成功之后,它将进入“确认”阶段。如果有任何异常,它将进入“取消”阶段。


Confirm阶段

6种最常见分布式事务解决方案!


取消阶段
假设库存扣除失败,并且取消交易需要回滚。

6种最常见分布式事务解决方案!


TCC方案适用于对一致性要求极高的系统,例如与货币交易有关的系统。但是,可以看出它是基于补偿原理的。因此,有必要编写大量用于补偿交易的代码,这是相对多余的。但是,存在开源TCC框架,例如TCC事务。


3. 本地消息表

本地消息表分布式事务解决方案是国外的eBay提出的一套方案。

6种最常见分布式事务解决方案!

需要注意的是,在这种方案中,在A系统中,我们首先写业务表,然后写消息表,然后将消息发送到MQ,在B系统中,我们需要先写消息表,这是为了确保消息重复使用,为了确保消息使用的幂等性,我们可以使用数据的唯一键进行约束。

系统B执行成功后,需要通知系统A执行成功。此时,可以使用诸如Zookeeper之类的侦听器。 ZK监视执行是否成功并成功更新系统A。然后开始发送下一条消息。

A系统中需要有一个后台线程,以将A系统的状态连续确定为要确认的消息,设置超时机制,并在超时时将其重新发送给MQ。直到执行成功。

可以看出,本地消息表解决方案需要写入消息表中。如果在高并发方案中执行大量磁盘IO,则此解决方案不适用于高并发方案。

4.可靠的消息最终一致性方案
该解决方案基于本地消息表进行了优化。它不是基于本地消息表,而是基于MQ。例如,阿里的RocketMQ支持消息事务。


在该方案中,首先A系统需要向MQ中发送prepare消息,然后执行A系统的业务,写入数据库成功之后向MQ发送confirm消息,当消息为confirm状态时,B系统就可以消费到消息,消费成功之后返回ACK确认消息给MQ。


需要注意的是。需要保证B系统消费消息的幂等性,可以借助第三方系统。比如在redis中设置标识,标明已经消费过该消息,或者借助ZK基于分布式锁的原理,创建节点,重复消费消息,创建失败。


5.最大努力通知方案

最大努力通知型( Best-effort delivery)是最简单的一种柔性事务,适用于一些最终一致性时间敏感度低的业务,且被动方处理结果 不影响主动方的处理结果。典型的使用场景:如银行通知、商户通知等。


在该系统中,A系统执行完本地事务,向MQ发送消息,最大努力通知服务消费消息,比如消息服务,然后调用B系统的接口,执行B系统的本地事务,如果B系统执行成功则OK,否则不断重试,重复多次之后还是失败的话就放弃执行。