分布式事务的解决方案:2PC,TCC以及基于消息的最终一致性

何时选择利用TCC实现的事务
然而基于补偿的事务形态也并非能实现所有的需求,如以下场景:某笔订单完成时,同时扣掉用户的现金,但交易未完成,也未被取消时,不能让客户看到钱变少了。

这时我们可以引入TCC,其流程如下:

订单服务创建订单
订单服务发送远程调用到现金服务,冻结客户的现金
提交订单服务数据
订单服务发送远程调用到现金服务,扣除客户冻结的现金
以上是正常完成的流程,若为异常流程,则需要发送远程调用请求到现金服务,撤销冻结的金额。

以上流程比基于补偿实现的事务的流程要复杂,同时开发的工作量也更多:

订单服务编写创建订单的逻辑
现金服务编写冻结现金的逻辑
现金服务编写扣除现金的逻辑
现金服务编写解冻现金的逻辑
TCC实际上是最为复杂的一种情况,其能处理所有的业务场景,但无论出于性能上的考虑,还是开发复杂度上的考虑,都应该尽量避免该类事务。

2PC事务
其适用于参与者较少,单个本地事务执行时间较少,并且参与者自身可用性很高的场景,否则,其很可能导致性能下降严重。

并非一种事务形态就能打遍天下
通过分析我们可以发现,并不存在一种事务形态能解决所有的问题,我们需要根据特定的业务场景选择合适的事务形态。甚至于有时需要混合多种事务形态才能更好的完成目标,如 上面提到的 订单、积分、钱包混合的场景:订单的成功与否需要依赖于钱包的余额,但不依赖于积分的多少,因此可以混合基于消息的事务形态以加积分 及 基于补偿的事务形态以确保扣钱成功,从而得到一个性能更好,编码量更少的形态。

然而目前很多框架都专注于某单一方面的事务形态,如TCC单独一个框架,可靠消息单独一个框架,SAGA单独一个框架,他们各自独立,容易导致以下问题:

由于前期只采用了其中一种类型事务的框架,因为工具目前只有锤子,引入其他工具又涉及测试、阅读代码等过程,因此把所有问题都看做钉子,导致性能偏低或者实现不够优雅
由于不同框架管理事务的形态可能不一致,导致不能很好的协调工作,如某一个TCC框架和另一个基于消息的事务框架无法很好融合。
解决方案
为了解决上面提到的问题,EasyTransaction这个基于Spring的分布式事务框架,实现了上述除2PC以外的所有事务形态,并提供了统一的使用接口,完美地解决了以上的问题。其主要特性如下:

一个框架包含多种事务形态,一个框架搞定所有类型的事务
多种事务形态可混合使用
高性能,若不启用框架的幂等功能,对业务数据库的额外消耗仅为写入25字节的一行
可选的框架幂等实现(包括调用次序错乱处理),大幅减轻业务开发工作量
业务代码可实现完全无入侵
支持嵌套事务
无需额外部署协调者,不同APP的服务协调自身发起的事务
分布式事务ID可关联业务ID,业务类型,APPID,便于监控各个业务的分布式事务执行情况

何时选择基于消息实现的事务?
基于消息实现的事务适用于分布式事务的提交或回滚只取决于事务发起方的业务需求,其他数据源的数据变更跟随发起方进行的业务场景。

扫描二维码关注公众号,回复: 11125657 查看本文章

举个例子,假设存在业务规则:某笔订单成功后,为用户加一定的积分。

在这条规则里,管理订单数据源的服务为事务发起方,管理积分数据源的服务为事务跟随者。

从这个过程可以看到,基于消息队列实现的事务存在以下操作:

订单服务创建订单,提交本地事务
订单服务发布一条消息
积分服务收到消息后加积分
我们可以看到它的整体流程是比较简单的,同时业务开发工作量也不大:

编写订单服务里订单创建的逻辑
编写积分服务里增加积分的逻辑
可以看到该事务形态过程简单,性能消耗小,发起方与跟随方之间的流量峰谷可以使用队列填平,同时业务开发工作量也基本与单机事务没有差别,都不需要编写反向的业务逻辑过程。因此基于消息队列实现的事务是我们除了单机事务外最优先考虑使用的形态。

发布了16 篇原创文章 · 获赞 1 · 访问量 426

猜你喜欢

转载自blog.csdn.net/wangziman/article/details/105742132