分布式事务的一些解决方案

文章介绍了分布式环境中解决事务一致性的AT模式和TCC模式。AT模式依赖全局锁确保一致性,而TCC模式通过Try-Confirm-Cancel三阶段人工编码实现。两种模式各有优劣,AT模式简单但存在最终一致性问题,TCC模式性能高但需编写额外代码。选择适合的方案取决于具体需求。
摘要由CSDN通过智能技术生成

目录

AT模式

介绍

一些问题

解决方案

总结

TCC模式

介绍

一些问题

解决方案

总结

分布式事务总结

参考资料


众所周知,在单体应用中处理事务时我们一般只需要加一个@Transactional注解就能保证事务中的一致性,但在现在广泛使用的分布式应用程序中,在跨多个服务时面临的事务问题中这种方法就失效了,我们需要保证最大是全局事务具有一致性,这个时候就要采取一些分布式事务的解决方案了,在这里我主要讲两种解决方案吧,一种是AT模式,一种是TCC模式。

当然在此之前需要介绍一些重点的名词

TC (Transaction Coordinator)事务协调者:维护全局和分支事务的状态,协调全局事务提交或回滚

TM (Transaction Manager) 事务管理器:定义全局事务的范围,开始全局事务,提交或回滚全局事务

RM(Resource Manager) 资源管理器 :管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚

AT模式

介绍

AT模式是一种分阶段提交的事务模型

上面就是这个过程的流程图

一般来说,TM会先开启一个全局事务,然后去调用各个分支(微服务)去执行分支事务,各个分支会接到指令后会执行sql并且提交,同时还会将事务中涉及到的记录的更新前后的两个状态都会记录在undolog里面,结束后RM会向TC报告事务是失败了还是成功了,当TC接收到所有分支事务都成功的消息时则告知各个RM本次全局事务成功,各个RM就会删除log数据,否则就会根据之前的log将数据回复回来,相当于全局事务做了回滚操作。

一些问题

大体来看AT模式确实能解决分布式事务的一致性问题,但考虑下在上图这种场景中,其实还是会出现一些问题

数据库记录的当前余额为100,有某个全局事务的一个分支事务1去执行将余额扣减10的一个操作,注意,各个分支事务处理时是直接执行并提交的,提交了后就把数据库的锁释放了,但是此时来了一个另外的一个事务2,它不在事务1的全局事务里面,可以算是一个独立的事务,此时也做了一个将余额扣减10的操作,余额就变成了80,事务2结束后TC发现刚刚那个全局事务有部分分支事务失败了,就让所有分支事务回滚,这是事务1那里就会将余额变成原来的100。这个过程中事务的扣减操作就相当于被覆盖掉了,显然这是不正确的。所以这种情况肯定是要解决的。

解决方案

为了解决上述问题,AT模式引入的一个全局锁,其实就是把一些信息记录到了一个表里,具体就是把当前分支事务id和要操作的表还有哪一行数据记录下来,每个分支事务在执行完sql后不会直接提交,而是会先去看一下表里面有没有其他事务操作的数据是和自己一样的,如果有,那就算是获取锁失败了,就会等待一段时间,最后还获取不到锁那就事务不提交就回滚了。

在了解的全局锁的机制后我们可以看上图这个过程,某个全局事务的分支事务1执行完sql后获取到了锁(因为原先表里面没有记录),事务1提交后会释放数据库锁,这是某个普通事务2就去获取数据库锁执行sql,之后回去获取全局锁才能提交,但肯定这时获取肯定最后会超时从而获取失败从而导致事务2不提交直接回滚,然后释放数据库锁(全局锁超时时间为图中所示,也就300ms,事务1接受到全局事务失败消息想要回滚时需要重新获取数据库锁来恢复数据,但数据库的超时时间一般大于全局锁的超时时间)。事务1在事务2释放数据库锁后就获取数据库锁从而恢复数据。这样的话就保证了事务2最终是失败的,还可以通知给用户,用户重试即可,而不是像没引入全局锁之前用户以为自己扣了实际上没扣成。

这里用全局锁机制而不是采取执行完sql不提交从而占有数据库锁来保证强一致性的方式是有好处的,就是如果用数据库的锁的话,那么在一个分支事务自身执行完后到TC通知它最终全局事务成功还是失败的这个时间内其他任何事务都是无法访问那个数据的,性能会差很多,但如果采用全局锁机制的话,因为全局锁是由TC管理的,虽然由TC管理的事务在那个时间内也无法访问,但是还是有非TC管理的事务还是能访问那一行数据的,这样性能就会好很多,这就是因为全局锁的粒度实际上是要比数据库锁要低的,所以性能上会好不少。

当然,由于锁粒度降低,必然会出现一种情况,一个不受TC管理的事务在那个时间间隙内也操作了那一行数据,由于不用获取全局锁,所以是可以操作成功的,但是,AT模式对这种情况也是有应对方法的。前面我们提到事务中操作的数据我们都记录的更新前后的数据,事务1会记录变更前100,变更后90,后来假设被一个不受TC管理的事务2也执行提交了扣减10,那么事务1回滚时就会知道当前数据是80,跟上一次自己记录的更新后数据90不一样,这时就会知道中间有别的事务执行提交了,这时就可以通过人工介入的方式来保证数据的最终一致性。

当然可能有人会觉得最后人工介入还麻烦,不如用原本数据库锁,但是在实际生产中,全局事务大多都是成功的,回滚终究是少数,这种情况也就在回滚的时候才有可能出现,并且由于分布式事务一般都是涉及到很多个微服务,链路比较长,耗时也比较长,这种事务一般是不会出现并发高的情况的(开发人员一般都会尽量规避出现这种情况),所以并发出现另一个事务在那个时间段内操作同一个数据的概率本来就低。所以即使我们采用了人工介入的方式也还是可以接受的,用不大的人工成本换了更高的性能还是ok的

总结

AT模式分支执行完sql直接提交事务释放数据库锁性能会很好,使用全局锁实现读写隔离,无代码侵入,框架自动完成回滚和提交。但是一致性是最终一致性,不是强一致性,并且还是使用了全局锁的机制,性能还是会受到一定限制。

TCC模式

介绍

TCC模式与AT模式非常相似,每阶段都是独立事务,不同的是TCC通过人工编码来实现数据恢复。需要实现三个方法。

  • Try :资源的检测和预留

  • Confirm:完成资源操作业务;要求Try成功Confirm一定要成功

  • Cancel:预留资源释放,可以理解为try的反向操作

这里用上图来解释这个过程。

try阶段先检查余额,不够的话就直接失败了,够的话就将原本余额减30,在冻结金额那个字段上加上30,然后后面根据TC的通知来决定执行哪个阶段,如果全局事务失败,就走cancel逻辑,把冻结金额减掉加回去就行,要是成功,那就把冻结金额减掉30,当作确实消费了30。

上面的图就是TCC的具体过程,其实和AT模式差不多,也是分成两个阶段,由一阶段的结果来决定二阶段全局事务是回滚还是提交。这里就不过多去论述了。

一些问题

虽然这种模式没有加全局锁会导致性能很好,但是也是有一些问题的。

最著名的两个问题就是空回滚和业务悬挂了。

空回滚

如上图所示,在TM调用每个分支时某些分支可能会因为网络等原因会阻塞,最终超时导致RM认为该分支事务失败报告给TC,然后TC要求全部分支事务执行回滚cancel逻辑,但是该分支还没有执行try,就直接执行cancel的话肯定会出错,毕竟没有扣除就直接补偿了。所以这种情况应该在cancel逻辑里面什么都不做。所以叫空回滚。

业务悬挂

接着空回滚的情况向下走,调用分支事务的try请求经过一段时间阻塞最终到达了分支,这个时候全局事务都结束了,所以再执行try肯定是不合适的,所以在这个try里面也是不应该做任何操作的。

解决方案

上图是解决这种问题的一个方案

这里面是建立了一张表,来记录冻结金额和事务id以及事务的状态。

这个时候再重新走一遍刚才的try-confirm/cancel 逻辑。

try操作里面向表中加入一条记录,这样后面如果是confirm逻辑的话就正常删除记录就可以,如果是cancel逻辑,那就修改一下事务状态变为cancel即可。

这个时候再来看一下空回滚和业务悬挂的问题。

空回滚:此时执行cancel逻辑会先查记录改状态,发现查不到(就是try未执行)就不走任何逻辑直接返回就行。

业务悬挂:若全局事务结束后再来执行try逻辑时新增记录时发现cancel逻辑执行时已经插入了一条状态为cancel的记录,所以此时try操作里面就知道cancel已经执行过了,就直接返回就行。

总结

TCC模式相比于AT模式没有快照机制,没有全局锁,性能很好,而且不依赖数据库的事务机制,可以应用于非关系数据库如redis中。但是三个阶段代码都需要人为编写,有代码侵入,编写起来也比较麻烦。

分布式事务总结

这里我介绍了两种分布式事务的解决办法,都是不错的方案,总之图方便省事可以用第一种AT模式,性能还可以,不用编写代码,挺通用的。第二种TCC模式的话我了解到的是目前国内某些做金融领域的互联网公司是采取的这种,这种方案性能最强,而且三个阶段都由自己控制,上限比较高吧。当然关于分布式事务的解决方案肯定不止这两种 ,在一些要求不那么高的系统里面也会有本地消息表加消息队列的一种解决方案,总的来说,没有最好的方案,只有适合自己需求的方案,我们所要做的就是学习不同种解决方案的差异来以此来选出最合适的解决方案。

参考资料

黑马程序员分布式事务讲解

​​​​​后端训练营结课作业

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值