分布式事务.补偿事务

解决思路

XA方案问题分析:

1. 基于数据库的XA协议本质上就是两阶段提交,但由于性能原因在互联网高并发场景下并不适用

2. 2PC是在正常情况下的方案,回滚只是业务异常回滚。如果某个资源挂了呢?怎么保证交易的原子性A,从而保证数据一致性C呢?

3. 2PC正常情况下,怎么处理分布式事务隔离呢? 最好的方式是 串行,可行吗?

改进方案:

1. TM挨个调用 RM,如果出现异常,就对已经成功提交的进行补偿,比如:前面RM已提交了,后边的RM调用异常,那么就让前面的RM业务回滚。这种朴素的模型就是Saga

2. Saga这种方式并不能保证隔离性,于是出现了TCC

3. 还有像Ebay提出的基于消息表,即可靠消息最终一致模型,但本质上这也属于Saga模式的一种特定实现

思想有两个:

  1. 基于应用共享事务记录执行轨迹;
  2. 然后通过异步重试确保交易最终一致(这也使得这种方式不适用那些业务上不允许补偿回滚的场景)。

       这类分布式事务场景并不是微服务才出现的,在SOA时代其实就有了,常见的Saga、TCC、可靠消息最终一致等模型也都是很多年前就有了,只是最近几年随着微服务兴起,这些方案又重新被人关注了起来。

TCC

       TCC事务补偿是基于2PC实现的业务层事务控制方案,它是Try、Confirm和Cancel三个单词的首字母,含义如下:

       1、Try 检查及预留业务资源完成提交事务前的检查,并预留好资源。

       2、Confirm 确定执行业务操作

              对try阶段预留的资源正式执行。

       3、Cancel 取消执行业务操作

              对try阶段预留的资源释放。

注意:操作必须支持幂等。原因是调用报错会多次调用。

转账场景举例:

我们需要操作的目标字段,都要添加一个相关的冻结字段,try操作是操作冻结字段,cc操作时,将冻结的数值更新到目标字段。
示例如下:
  <!--try逻辑-->
  <update id="increaseMoney">
    UPDATE company SET frozen = frozen + #{money}
    WHERE id = #{id}
  </update>

  <!--confirm逻辑-->
  <update id="confirmIncreaseMoney">
    UPDATE company SET money = money + #{money},frozen = frozen - #{money}
    WHERE id = #{id}
  </update>

  <!--cancel逻辑-->
  <update id="cancelIncreaseMoney">
    UPDATE company SET frozen = frozen - #{money}
    WHERE id = #{id}
  </update>

Sagas

       事务模型(最终一致性)

       Saga的核心就是补偿,一阶段就是服务的正常顺序调用(数据库事务正常提交),如果都执行成功,则第二阶段则什么都不做;但如果其中有执行发生异常,则依次调用其补偿服务(一般多逆序调用未已执行服务的反交易)来保证整个交易的一致性。

       这种模式,是使用一个反向的业务操作,来撤销之前的业务操作。

       其核心思想是将长事务拆分为多个本地短事务,由 Saga 事务协调器协调,如果正常结束那就正常完成,如果某个步骤失败,则根据相反顺序一次调用补偿操作。

Saga 理论出自 Hector & Kenneth 1987发表的论文 Sagas。

saga模式的实现,是长事务解决方案。

Saga 是一种补偿协议,在 Saga 模式下,分布式事务内有多个参与者,每一个参与者都是一个冲正补偿服务,需要用户根据业务场景实现其正向操作和逆向回滚操作。

如图:T1~T3都是正向的业务流程,都对应着一个冲正逆向操作C1~C3

分布式事务执行过程中,依次执行各参与者的正向操作,如果所有正向操作均执行成功,那么分布式事务提交。如果任何一个正向操作执行失败,那么分布式事务会退回去执行前面各参与者的逆向回滚操作,回滚已提交的参与者,使分布式事务回到初始状态。

Saga 正向服务与补偿服务也需要业务开发者实现。因此是业务入侵的。

Saga 模式下分布式事务通常是由事件驱动的,各个参与者之间是异步执行的,Saga 模式是一种长事务解决方案。

 TCC&SAGA

1. 调用机制不同

       TCC 是按个 确认金额够不够。然后才是提交。

       Saga 是按个 直接扣钱,不够再补偿回来。

2. 异常补偿机制不同

       TCC 需要添加一个冬季金额,原金额不变,执行都没问题后在一块提交事务。

       Saga 直接把钱扣了,如果没扣成功,在加回来。à 可能有假数据。

基于消息的事务

基于可靠消息最终一致,一阶段服务正常调用,同时同事务记录消息表,二阶段则进行消息的投递,消费。虽然跟TCC和Saga列在一块,都属于补偿事务机制,但对RM的管理机制是不同的。

本地消息

本地消息表与业务数据表处于同一个数据库中,这样就能利用本地事务来保证在对这两个表的操作满足事务特性,并且使用了消息队列来保证最终一致性。

事务消息

有一些第三方的MQ是支持事务消息的,比如RocketMQ,他们支持事务消息的方式也是类似于采用的二阶段提交,但是市面上一些主流的MQ都是不支持事务消息的,比如 RabbitMQ 和 Kafka 都不支持。

       保证消息发送成功后,在决定是否提交和回滚本地事务。

TCC设计注意

  • ➢业务模型分2阶段设计
  • ➢并发控制
  • ➢允许空回滚
  • ➢防悬挂控制
  • ➢幂等控制

业务模型分 2 阶段设计

用户接入 TCC ,最重要的是考虑如何将自己的业务模型拆成两阶段来实现。

以“扣钱”场景为例,在接入 TCC 前,对 A 账户的扣钱,只需一条更新账户余额的 SQL 便能完成;但是在接入 TCC 之后,用户就需要考虑如何将原来一步就能完成的扣钱操作,拆成两阶段,实现成三个方法,并且保证一阶段 Try 成功的话 二阶段 Confirm 一定能成功。

如上图所示,Try 方法作为一阶段准备方法,需要做资源的检查和预留。在扣钱场景下,Try 要做的事情是就是检查账户余额是否充足,预留转账资金,预留的方式就是冻结 A 账户的 转账资金。Try 方法执行之后,账号 A 余额虽然还是 100,但是其中 30 元已经被冻结了,不能被其他事务使用。

二阶段 Confirm 方法执行真正的扣钱操作。Confirm 会使用 Try 阶段冻结的资金,执行账号扣款。Confirm 方法执行之后,账号 A 在一阶段中冻结的 30 元已经被扣除,账号 A 余额变成 70 元 。

如果二阶段是回滚的话,就需要在 Cancel 方法内释放一阶段 Try 冻结的 30 元,使账号 A 的回到初始状态,100 元全部可用。

用户接入 TCC 模式,最重要的事情就是考虑如何将业务模型拆成 2 阶段,实现成 TCC 的 3 个方法,并且保证 Try 成功 Confirm 一定能成功。相对于 AT 模式,TCC 模式对业务代码有一定的侵入性,但是 TCC 模式无 AT 模式的全局行锁,TCC 性能会比 AT 模式高很多。

 允许空回滚

Cancel 接口设计时需要允许空回滚。在 Try 接口因为丢包时没有收到,事务管理器会触发回滚,这时会触发 Cancel 接口,这时 Cancel 执行时发现没有对应的事务 xid 或主键时,需要返回回滚成功。让事务服务管理器认为已回滚,否则会不断重试,而 Cancel 又没有对应的业务数据可以进行回滚。

防悬挂控制 

悬挂的意思是:Cancel Try 接口先执行,出现的原因是 Try 由于网络拥堵而超时,事务管理器生成回滚,触发 Cancel 接口,而最终又收到了 Try 接口调用,但是 Cancel Try 先到。按照前面允许空回滚的逻辑,回滚会返回成功,事务管理器认为事务已回滚成功,则此时的 Try 接口不应该执行,否则会产生数据不一致,所以我们在 Cancel 空回滚返回成功之前先记录该条事务 xid 或业务主键,标识这条记录已经回滚过,Try 接口先检查这条事务xid或业务主键如果已经标记为回滚成功过,则不执行 Try 的业务操作。

幂等控制

幂等性的意思是:对同一个系统,使用同样的条件,一次请求和重复的多次请求对系统资源的影响是一致的。因为网络抖动或拥堵可能会超时,事务管理器会对资源进行重试操作,所以很可能一个业务操作会被重复调用,为了不因为重复调用而多次占用资源,需要对服务设计时进行幂等控制,通常我们可以用事务 xid 或业务主键判重来控制。

Saga使用场景 

 Saga 模式适用于业务流程长且需要保证事务最终一致性的业务系统,Saga 模式一阶段就会提交本地事务,无锁、长流程情况下可以保证性能。

事务参与者可能是其它公司的服务或者是遗留系统的服务,无法进行改造和提供 TCC 要求的接口,可以使用 Saga 模式。

Saga模式的优势是:

一阶段提交本地数据库事务,无锁,高性能;

参与者可以采用事务驱动异步执行,高吞吐;

补偿服务即正向服务的“反向”,易于理解,易于实现;

缺点:Saga 模式由于一阶段已经提交本地数据库事务,且没有进行“预留”动作,所以不能保证隔离性。后续会讲到对于缺乏隔离性的应对措施。

与TCC实践经验相同的是,Saga 模式中,每个事务参与者的冲正、逆向操作,需要支持:

空补偿:逆向操作早于正向操作时;

防悬挂控制:空补偿后要拒绝正向操作

幂等

实现组件

Bytetcc

Bytetcc是由北京新奥集团工程师开发,是一个兼容JTA规范的基于TCC机制的分布式事务管理器。目前开发到了第五版,稳定版本为第四版。

特点:

1、支持Spring容器的声明式事务管理;

2、支持普通事务、TCC事务、saga事务等事务机制;

3、支持多数据源、跨应用、跨服务器等分布式事务场景;

4、支持长事务;

5、支持dubbo服务框架;

6、支持spring cloud;

TCC-transaction

DTX

传统分布式事务需保证ACID属性,强调一致性,要求强一致;而BASE则是与之相对立的理论,认为为了可用性(Availability)而牺牲部分一致性(Consistency)可以显著的提升系统的可伸缩性,这就是异步操作。蚂蚁金服分布式事务产品DTX分别基于两种理论实现了两种模式:基于BASE理论的TCC模式和基于ACID理论的FMT模式。

为了解决 TCC 模式的易用性问题,蚂蚁分布式事务后来又推出了框架托管模式(Framework-managed transactions,简称 FMT)。FMT是一种无侵入的分布式事务解决方案,该模式解决了分布式事务的易用性问题,在该模式下,开发者只需关注一阶段操作,框架会自动解析SQL语义,生成二阶段提交和回滚操作,使分布式事务的接入更便捷,该模式下对业务代码几乎无侵入,框架能够“自动化”地解决分布式架构下的数据一致性问题。

“DTX本身是有嵌套的,如果调了一个服务,可能它下面还调用了其它服务,也是分布式的,从而形成多级复杂嵌套。DTX是一个方法论级的保证,不管分多少级,只要层层提交成功了,最终就都能成功提交。”尹博学介绍。DTX本身带有实时监控,可以监控实时的事务信息,包括事务数、成功率、平均耗时等,也可以与链路监控相结合,根据DTX上报的实时信息,提供历史趋势图、同比/环比分析、报警等功能。

异步模式让DTX具有极强的可扩展性,交易量翻多少倍都可以支持。

蚂蚁金服的分布式事务有两个名字:对内叫XTS,ExtendedTransaction Service可扩展事务服务;对外叫DTX,Distributed Transaction-eXtended分布式事务


“首先我们假想这样一种场景:转账服务,从银行 A 某个账户转 100 元钱到银行 B 的某个账户,银行 A 和银行 B 可以认为是两个单独的系统,也就是两套单独的数据库。

我们将账户系统简化成只有账户和余额 2 个字段,并且为了适应 DTS 的两阶段设计要求,业务上又增加了一个冻结金额(冻结金额是指在一笔转账期间,在一阶段的时候使用该字段临时存储转账金额,该转账额度不能被使用,只有等这笔分布式事务全部提交成功时,才会真正的计入可用余额)。按这样的设计,用户的可用余额等于账户余额减去冻结金额。这点是理解参与者设计的关键,也是 DTS 保证最终一致的业务约束。”

在try阶段并没有对银行A和B数据库中的余额字段做操作,而是对冻结金额做的操作,对应A银行预留资源操作是对冻结金额加上100元,这时候A银行账号上可用钱为余额字段-冻结金额;对应B银行的操作是对冻结金额上减去100,这时候B银行账号上可用的钱为余额字段-冻结金额。

如果事务协调器调用银行A和银行B的try方法有一个失败了(比如银行A的账户余额不够了),则调用cancle进行回滚操作(具体是对冻结金额做反向操作)。如果调用try方法都OK了,则进入confirm阶段,confirm阶段则不做资源检查,直接做业务操作,对应银行A要在账户余额减去100,然后冻金额减去100;对应银行B要对账户余额字段加上100,然后冻结金额加上100。

最关心的,如果confirm阶段如果有一个参与者失败了,该如何处理,其实上面操作都是xts-client做的,还有一个xts-server专门做事务补偿的。

EasyTransaction

EasyTransaction(后简称ET)的目标也是构建出一个全面分布式事务解决方案,到目前为止其包含TCC,自动补偿(Seata AT),手动补偿,可靠事务消息、Saga事务等等多种形态。

Hmily

Hmily是由碧桂园工程师开发,高性能异步分布式事务TCC框架。

Hmily 是一款高性能分布式事务 tcc 开源框架。基于java语言来开发(JDK1.8),支持 Dubbo、Spring Cloud、Motan 等 RPC 框架进行分布式事务。

框架特性

支持嵌套事务(Nested transaction support)

采用disruptor框架进行事务日志的异步读写,与RPC框架的性能毫无差别

支持SpringBoot-starter 项目启动,使用简单

RPC框架支持 : dubbo,motan,springcloud

本地事务存储支持 : redis,mongodb,zookeeper,file,mysql。

事务日志序列化支持 :java,hessian,kryo,protostuff

TX-LCN

SpringCloud系列——TX-LCN分布式事务管理 - huanzi-qch - 博客园


参考

分布式事务的四种解决方案 - 无敌是多么寂寞啊 - 博客园

 SpringCloud系列——TX-LCN分布式事务管理 - huanzi-qch - 博客园

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闲猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值