这篇文章将介绍分布式事务中的多种实现方案,及各种分布式事务方案的实现原理、事务执行过程、优缺点,读完这篇文章相信你会对2PC、3PC、TCC、MQ事务消息有个详细的了解
分布式事务的处理方法有哪些?
XA协议:基于分布式事务协议,主要由事务管理器和本地资源管理器组成,事务管理器是一个全局调度者,负责本地资源管理器统一的提交或回滚事务。mysql、oracle均已支持XA协议
2PC(两阶段提交)
在这里插入图片描述基本流程:
-
Prepare预备阶段
:预执行需要执行的sql,但不提交事务 -
Commit提交阶段
:提交事务
不足:
- 数据不一致:在事务管理器向所有服务发送提交事务Commit阶段时,某些参与者可能发生网络抖动,无法正常接收到Commit请求,从而导致每个参与者的数据不一致
- 超时导致同步阻塞:当有一个参与者出现通信超时,其余所有参与者将一直阻塞无法释放资源
- 单点故障风险:如图可知,资源管理器统一协调所有参与者,一旦资源管理器出现故障,则参与者无法完成Commit操作,会一直处于阻塞状态。尽管资源管理器会重新选举,当还是无法解决之前遗留的阻塞问题。
- 性能问题:所有参与者在事务提交阶段处于同步阻塞状态,占用系统资源,容易导致性能瓶颈
3PC(三阶段提交)
在这里插入图片描述基本流程:
-
CanCommit
:协调者向所有参与者发送CanCommit命令,,询问是否可以执行事务提交操作。如果全部响应YES则进入下一个阶段 -
PreCommit
:预提交事务操作,询问是否可以进行事务的预提交操作,参与者接收到PreCommit请求后,如参与者成功的执行了事务操作,则返回Yes响应,进入最终commit阶段。一旦参与者中有向协调者发送了No响应,或因网络造成超时,协调者没有接到参与者的响应,协调者向所有参与者发送abort请求,参与者接受abort命令执行事务的中断。 -
DoCommit
:前两个阶段都返回YES响应后,协调者向参与者发送DoCommit正式提交事务命令,如果没有接收到ACK响应,则向其它参与者发送abort命令,执行事务中断
相对于2PC的改进:
- <mark style="box-sizing: border-box; outline: 0px; background-color: rgb(248, 248, 64); color: rgb(0, 0, 0); overflow-wrap: break-word;">在协调者与参与者加入了超时机制</mark>,当在一定时间内未收到协调者的commit请求,会自动提交事务,不会一致阻塞等待
- 3PC将2PC的Prepare准备阶段拆分为 2 个阶段,插入了一个 ==PreCommit ==阶段,解决原先在2PC中,由于协调者发生故障而导致参与者无法知晓是否commit或abort的状态而产生的相当长的阻塞问题得以解决
- 有了自动提交事务,一旦发生单点故障,事务也可以自动提交
不足:
- 在发送abort命令的时候,如果因为网络原因部分参与者没有接收到请求,还是会导致数据不一致性问题
TCC(事务补偿)
TCC也是实现分布式事务的一种方案,不过它是属于应用层面的,非基于XA协议,需要我们编写业务代码来实现类似于2PC的命令功能
核心思想:
<mark style="box-sizing: border-box; outline: 0px; background-color: rgb(248, 248, 64); color: rgb(0, 0, 0); overflow-wrap: break-word;">对于每一个操作都有其对应的确认(Confirm)和补偿(Cancel)方法,且它们是幂等性的</mark>
基本流程:
-
Try
:通过Try操作来检查、预留需要的库存,比如需要2台iphone12并进行冻结 -
Confirm
:真正执行业务操作,在之前预留的资源基础上完成购买和创建订单 -
Cancel
:在Try阶段预留2台iphone12失败,则取消所有业务资源的预留(也就是恢复try之前)
优点:
- 性能提升:依据业务来控制资源锁的粒度,不会控制整个资源
- 数据最终一致性:基于Confirm和Cancel的幂等性,能够保证事务完成或取消
- 可靠性:解决了XA协议中事务管理器单点故障问题,直接由业务活动发起并控制整个业务活动,业务活动管理器也变成了多点(集群)
不足:
7. 代码侵入性强:之前也说需要在业务层面实现这三个命令,实际就是需要实现三个接口
8. 开发成本高:因为需要自己实现三个阶段的业务代码,开发量很大;且如果要保证数据一致性,需要自己实现幂等性
RocketMQ事务消息(最终一致性)
在这里插入图片描述基本流程:
- 事务主动方向消息集群发送消息,但这个消息不能被消费
- 事务主动方执行本地事务A,执行成功后根据消息地址去修改消息状态为commit或rollback,如果是commit则会被事务被动方消费,否则将删除消息
- 事务被动方读取消息,然后执行本地事务B
- 事务被动方将事务B执行结果返回到MQ Server
- 事务主动方读取接收方事务处理结果
异常情况1:如果事务主动方发送commit or rollback消息失败,未到达消息集群
- 消息集群(MQ Server)会发起消息回查
- 事务主动方收到回查消息后,会检查本地事务的执行结果
- 根据本地事务的执行结果重新发送commit or rollback消息
- MQ Server根据接收到的消息(commit or rollback)判断消息是否可消费或直接删除
异常情况2:接收方消费失败或消费超时
一直<mark style="box-sizing: border-box; outline: 0px; background-color: rgb(248, 248, 64); color: rgb(0, 0, 0); overflow-wrap: break-word;">重试消费</mark>,直到事务被动方消费消息成功,整个过程可能会导致重复消费问题,所以业务逻辑需要保证幂等性
异常情况3:消息已消费,但接收方业务处理失败
通过MQ Server通知发送方进行补偿或事务回滚
优点:
- 系统解耦:消息之间独立存储,系统之间消息完成事务
- 复杂度低,实现简单
缺点:
- 一次消息需要发送两次请求(half消息+commit or rollback)
- 业务中需要实现消息状态回查接口
Spring中已经有了事务,还需要使用事务消息吗?
如果是在Spring框架下,可以将发送消息的逻辑绑定到本地事务中,发消息失败就抛出异常,回滚事务,以此来保证本地事务与发送消息的原子性
网友评论