再论分布式事务:从理论到实践

本文补充一种分布式事务解决方法:Best Effort.

Best Effort

  best effort即尽最大努力交付,主要用于在这样一种场景:不同的服务平台之间的事务性保证。比如我们在电商购物,使用支付宝支付;又比如玩网游的时候,通过App Store充值。拿购物为例,电商平台与支付平台是相互独立的,隶属于不同的公司,即使是同一个公司也很可能是独立的部门。因此,这两个平台是不可能使用同一套分布式事务框架的,2PC不行,tcc也不行,异步消息也不行。

  其实在上面电商平台与支付平台的例子中,涉及到多重事务性:

  电商平台与支付平台之间的事务性:电商的下单操作与支付平台扣款的原子性,不能说支付平台扣了用户的钱,但电商平台不发货;或者说,电商平台先发了货,支付平台没有扣用户的钱;   电商平台内部的事务性:比如订单与优惠券、红包等;   支付平台内部的事务性:比如用户账户、商户账户等;

  不管是因为技术原因,还是说安全策略,支付平台只会提供给电商平台一些Http接口,即开放支付服务。电商平台在发出一笔支付请求后,是不大可能立刻获得支付是成功还是失败的确切消息,更多的时候应该是请求已被接受,处理中。这个时候支付平台已经将该请求持久化,保证一定会处理这个请求。当支付平台处理完这个支付请求之后,怎么将结果通知给电商平台呢,要么是电商平台定时轮训,要么是电商平台在初始支付请求的时候携带一个callback,提供给支付平台回调。在这篇文章中提到,支付宝采用的是回调的形式:

  “做过支付宝交易接口的同学都知道,我们一般会在支付宝的回调页面和接口里,解密参数,然后调用系统中更新交易状态相关的服务,将订单更新为付款成功。同时,只有当我们回调页面中输出了success字样或者标识业务处理成功相应状态码时,支付宝才会停止回调请求。否则,支付宝会每间隔一段时间后,再向客户方发起回调请求,直到输出成功标识为止。”

  这个例子,绘制成流程图就是这样样子的:

 再想想上文提到的银行转账的例子,很可能也是采用best effort这种模式,银行之间肯定是相互独立的。首先是本地银行先扣款,然后通知另外一个银行加款,但为什么对方加款失败,没有通知到本地银行,就不清楚了

分布式事务解决方案比较

  在这里主要通过以下几个维度来对比分析:

  • 一致性
  • 资源锁粒度:是否要利用到数据库的锁机制,加锁的粒度
  • 子事务串并行:组成 一个事务的多个子事务是并发执行,还是串行执行
  • 回滚(补偿):是哪个层面的回滚(补偿)、回滚的代价

  注意,上面提到的回滚和补偿是一个意思,“回滚”不局限于DB里面的术语,而是指通用的对某个操作的逆反操作

 2PC的强一致性依赖于数据库,而TCC的强一致性依赖于应用层的Commit与cancel。异步消息,1PC,best effort都只保证最终一致性(且最终一致性还可能依赖于人工介入,是否应该算弱一致性?)

  2PC需要对整个资源加锁,因此不适用于高并发的分布式场景;而tcc只对需要的资源进行加锁,加锁的粒度小,且try commit Cancel都是本地短事务,因此能在保证强一致性的同时最大化提高系统可用性。而异步消息,1PC,best effort都是先提交一部分事务,无需加锁。

  2PC是有数据库来保证回滚,而TCC是应用层实现回滚:为每一个try操作提供一个对应的cancel操作。而异步消息,1PC适用于理论上一定会成功的场景,难以回滚。best effort这种模式,需要服务的调用者实现完整的一个事务操作用于回滚,比如支付失败的情况。数据库的回滚较简单,而应用层的回滚较为困难,更重要的是,回滚也需要作为一个事务进行,部分回滚失败的情况最可怕。

  至于子事务的串行、并行,在其他文章中并没有看见过相关讨论,但肯定是实践的时候必须要考虑的问题。即一个分布式事务肯定是由多个分支事务组成,那么多个分支事务是并发执行,还是串行执行呢?特别对于2PC,TCC这些分为多个阶段的解决方案,每个阶段是并发,还是串行呢

分支事务串并行与LPO

首先,对于异步消息,best effort,肯定都是串行的,其中一个分支事务完成之后,再去做另一个分支事务。

  但对于2PC,TCC,理论上看起来是并行的,但工程实践中有可以串行。以2PC为例

  2PC从介绍的文章来看,多属于并行:即协调者同时让参与者prepare,然后在第二阶段同时通知参与者commit或者abort,下面两个图说明了这个并行的过程。

上面分别是两阶段提交协议成功commit与失败abort的情况,可以看出在prepare阶段,多个参与者是并行的。

  而2PC的串行模式,就是说,先通知一个参与者准备,成功的话再通知另一个参与者准备,即准备阶段是串行的。下图来自支付宝:

注意 上面的图示,第二阶段(commit 或者 abort)也画成串行的,这里应该是可以并行的。

  那么串行、并行的区别在于哪里呢

  (1)并行效率高,整个事务的耗时更少;

  (2)而串行在prepare阶段失败的情况下,只需部分回滚;

  在工程实践中为什么会采用串行这种方式呢,这是另外一个重要的优化: “最末参与者优化”(Last Participant Optimization,术语来自支付宝),即允许两阶段提交协议中有一个参与者不实现“准备”操作,在其余参与者都prepare ok的情况下,直接提交自己的分式事务。

  网络上关于LPO的介绍并不多,在oracle官网Logging Last Resource Transaction Optimization中有如下介绍:

The LLR resource uses a local transaction for its transaction work. The WebLogic Server transaction manager prepares all other resources in the transaction and then determines the commit decision for the global transaction based on the outcome of the LLR resource’s local transaction.  

  最末参与者优化的原理如下图所示:

本质上,LPO是将最后一个参与者的准备操作与提交/放弃操作合并成一个提交操作,这样提高了分布式事务的执行效率。也可以看到,要使用LPO,在prepare阶段一定是串行的。

  在一些业务场景,是无需单独的协调者,即事务的发起者同时是组成事务的分支事务。比如支付宝的例子,业务服务和账户服务组成一个分布式事务,在业务服务上发起事务请求,因此没有单独的协调者服务器,使用LPO也比较适合。

再论TCC

  前面已经介绍过TCC的三个阶段,Try负责预留资源,Commit提交预留的资源,Cancel“回滚”预留的资源。那么某一个分支事务的Try操作是否可以直接做Commit所做的事情呢,即Try操作直接提交分支事务。在这种情况下,如果所有分支事务的Try阶段都返回OK,那么该分支事务的Commit就什么都不用做,如果需要Cancel,那么就实现回滚。

  当然,我看到的更多形式,比如支付宝的XTS,都只是冻结资源:加额外的字段,表明有多少数量的资源处于特殊状态。

  我们以一个扣款操作作为分支事务,比如要从账户A扣除100元。如果Try阶段直接执行事务,那么就从A的账户上真正扣除了,而Cancel阶段则加上100,看起来很容易;如果Try阶段只是冻结,那么就会复杂一些,一个可行的方案增加forzen字段的值,同时扣除账户。

  但如果考虑加款操作作为分支事务,Try阶段直接执行事务的话,很可能出现cancel阶段钱不够的情况(假设资金不能为负)

  因此,个人觉得,TCC框架是不用关心具体形式的,业务只需向框架注册这三个操作就行了,具体怎么操作,完全取决于业务,能满足业务的需求就行。

实践案例

 在龙果学院推出的课程《微服务架构的分布式事务解决方案》中,综合运用了各种分布式事务解决方案,如下如所示:

在上图中,使用了三种分布式事务解决办法:

  (1)基于可靠消息的最终一致性方案(异步确保型),这个使用比较广,适用于分支事务大概率成功的情况;

  上图中使用于:对应支付系统会计异步记账业务,银行通知结果信息存储与驱动订单处理

  (2)TCC事务补偿性方案,使用在同时需要保证一致性与高性能的场景

  对应上图中支付系统的订单账户操作:订单处理,资金账户处理,积分账户处理

  (3)best effort,最大努力通知型方案,适用于跨平台之间的事务原子性保证

  对应上图中支付系统的商户业务通知场景

原文发布于微信公众号 - IT技术精选文摘(ITHK01)

原文发表时间:2017-11-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏BestSDK

英特尔曝出重大安全漏洞:亚马逊、微软等众多云服务受影响

该漏洞存在于英特尔的 x86 硬件之中,无法通过微码升级来解决,必须在系统层面通过安装软件、或者购买没有设计缺陷的新处理器来解决——所以包括苹果 64 位 ma...

2883
来自专栏纯洁的微笑

电商平台备战促销季的运维秘诀——高可用服务层

1202
来自专栏BeJavaGod

RabbitMQ 一二事(4) - 路由模式介绍

路由模式其实和订阅模式差不多,只不过交换机的类型不同而已 ? 路由模式可以用下图来表示,比订阅模式多了一个key,举个栗子就是根据不同的人群来订阅公众号,来收取...

3385
来自专栏张戈的专栏

服务器日志备份超节省空间的思路

这两天,监控频繁发来服务器磁盘空间 90%+的报警,打扰睡觉不说,塞得满满的总是不舒服的。刚来公司才 20 天,对于部分细节上的运维了解得还不是很到位,比如这备...

4156
来自专栏全华班

微信小程序开发环境搭建

微信小程序是当前程序员讨论的相当火的一个名词了,当前App开发人员有个担心,微信小程序的到来会不会给移动端App带来一个寒冬。不管微信小程序是否能颠覆当今的开发...

7206
来自专栏魏琼东

基于DotNet构件技术的企业级敏捷软件开发平台 - AgileEAS.NET平台开发指南 - 实现业务

业务分层         依据行业经验来看,分层是解决复杂问题的简单方法,通过分层,可以把一个复杂问题分解为不同层次应用的小问题,解决各层小问题的难度小于总的问...

19510
来自专栏IT笔记

Lepus搭建企业级数据库全方位监控系统

Lepus(天兔)数据库企业监控系统是一套由专业DBA针对互联网企业开发的一款专业、强大的企业数据库监控管理系统,企业通过Lepus可以对数据库的实时健康和各种...

1735
来自专栏JAVA高级架构

电商平台备战促销季的运维秘诀——高可用服务层

1162
来自专栏架构师之路

小小的IP,大大的耦合,你痛过吗?

什么是耦合? 耦合,是架构中,本来不相干的代码、模块、服务、系统因为某些原因联系在一起,各自独立性差,影响则相互影响,变动则相互变动的一种架构状态。 感官上,...

4616
来自专栏GA小站

Google Analytics数据保护政策变更解读(201804)

相信很多管理员一大早就收到一封谷歌分析的关于数据保存和数据保护协议变更的通知邮件,有些人就问,这会不会对Clientid,Userid有什么影响,或对我们有什么...

752

扫码关注云+社区

领取腾讯云代金券