vlambda博客
学习文章列表

分布式事务设计与实践-TCC与Saga

分布式事务概述

分布式事务产生背景

  1. 传统的单体应用,商品服务,用户服务和交易服务程序都编写在一个单体项目中,通过启动一个JVM进程和连接一个数据库就能提供所有功能,这时就可以通过数据库的ACID来保证事务一致性。

  2. 随着业务的发展和技术的发展,衍生出了上图的微服务架构,各个服务都是一个独立的JVM进程,并且都有自己独立的数据库和缓存服务,这时通过数据库本地事务无法保证分布式业务的一致性。


分布式事务分类

1.刚性事务

  • 强一致性:各个业务操作必须在事务结束时全部成功,或者全部失败

  • XA模型

  • 满足CAP理论的CP


2.柔性事务

  • 保证最终一致性,事务结束后在可接受的时间范围内,可能出现短暂的不一致,最终会达成一致性

  • 满足CAP理论的AP,满足BASE理论

分布式事务设计与实践-TCC与Saga


刚性事务

满足传统事务特性

  • ACID(原子性,一致性,隔离性,持久性)

满足XA模型

  • 应用程序(AP):定义事务边界(事务开始与结束),并且访问事务边界内的资源

  • 资源管理器(RM):管理计算机共享的资源,资源即数据库

  • 事务管理器(TM):负责管理全局事务,分配全局事务ID,监测事务的执行速度,并负责事务的提交,回滚,失败恢复等。

分布式事务设计与实践-TCC与Saga

2PC(两阶段提交)是XA规范标准实现

分布式事务设计与实践-TCC与Saga

实现过程:

  1. AP发起一个全局事务,并且创建一个全局事务ID

  2. TM发起prepare投票,RM对投票进行表决

  3. RM都同意后,TM发起commit提交。其中任何一个prepare时不同意,TM都会发起rollback。

  4. 在commit过程中,发生宕机等异常,在服务重启后根据XA recover再次进行补偿,保证最终commit操作成功。

缺点:

  1. 同步阻塞模型

  2. 数据库资源锁定时间过程

  3. 全局锁(隔离级别串行化),并发能力低

  4. 不适合长事务

  5. 在一些异常情况下会有问题,在commit之后,回复给TM的ack丢失了,这时会导致TM不知道commit到底是否成功了,从而衍生出了三阶段提交来解决这个问题。


柔性事务

满足CAP模型的CP,柔性事务是对XA协议的妥协,它通过降低强一致性,从而减少数据库资源的锁定时间,提升系统可用性。

典型架构实现

  • TCC模型

  • Saga模型


TCC模型

TCC模型完全交由业务端实现,每个子业务都需要实现try-confirm-cancel接口,对业务侵入性很大。

资源锁定需要业务自主实现。

Try:尝试执行事务,完成业务检查,预留必要的资源。

confirm:真正执行业务,不做业务资源检查

cancel:释放try阶段预留的业务资源

我们通过一个转账案例来分析

用户A向用户B转账500元

汇款服务

Try:

1.检查A账户有效性,查看A账户是否存在或者未冻结。

2.检查账户余额是否大于等于500元

3.从A账户扣减500元,并且状态置为“转账中”

4.记录一个转账日志或者消息

Confirm:

1.将“转账中”的状态改为“正常”状态

2.生成转账流水,删除转账日志

Cancel:

1.账户余额加回500元

2.账户状态改为“正常”


收款服务

Try:

1.检查B账户是否正常

Confirm:

1.从转账日志或者消息中获取账户A往账户B转账500元

2.账户B增加500元

Cancel:

不做操作


Saga模型

Saga模型把一个分布式事务拆分成多个本地事务,每个本地事务都有对应的执行模块和补偿模块(对应TCC的confirm和cancel),当任何一个事务失败时,可以通过调用补偿模块来进行恢复,达到事务最终一致性。

Saga事务隔离性,业务层自己处理,通过冻结资源或者应用层加锁

Saga补偿方式:

向后恢复:补偿所有已完成的事务,本质就是所有已完成的本地事务进行回滚操作

向前恢复:重试失败的事务,假设每个子事务最终都会成功

分布式事务设计与实践-TCC与Saga


1.业务逻辑层调用加上事务注解@Around(“execution(* *(..)) && @annotation(TX)”),在业务入口方法加上这个事务注解,表明开启一个全局事务。

2.AOP在真正调用业务逻辑调用之前生成一个全局唯一事务TXID标示事务组,TXID保存在ThreadLocal变量中,方法开始前写入,完成后清除,并把TXID写入到数据库中,并把事务组的状态置为开始状态。

3.业务逻辑层调用数据访问层之前,通过RPCProxy代理记录当前调用请求参数。

4.如果业务正常,调用完成之后,当前方法的调用记录存档或者删除,把全局事务状态标示为成功。如果业务异常,抛出异常,全局事务标示为失败,定时任务进行异步反向补偿。

分布式事务设计与实践-TCC与Saga

Saga实现时序图

交易创建订单事务组正常流程:锁库存 -> 减红包 -> 创建订单


交易创建订单事务组异常流程:

总结:

1.分布式事务产生的背景是:一个业务功能涉及操作多个具有独立数据库和缓存的服务。

2.分布式事务分类:

  • 刚性事务:所有业务操作在事务结束是要么全部成功,要么全部失败,强一致性,并发低。

  • 柔性事务:可接受时间范围内,允许出现短暂的不一致性,最终达成一致性。

3.2PC两阶段提交:AP发起一个全局事务,TM发起一个prepare请求,RM在prepare阶段都执行成功,TM发起commit请求。在prepare阶段人意RM执行失败,TM发起rollback操作。

4.TCC主要涵盖try-confirm-cancel操作,这些操作都需要业务实现,堆业务侵入性比较大,而且比saga多一个提交阶段。

5.Saga是目前行业内落地较多的成功方案,由业务提供一个业务执行接口和一个补偿接口(需要满足幂等性)

  • 事务开启入口方法,开始一个aop环形切面,在方法调用前分配一个全局唯一事务ID,记录到数据库中并且初始化状态为开始。

  • 调用每个业务操作时,记录业务请求的参数,事务提交方法和回滚方法信息。

  • 所有业务操作正常执行完后,在aop环形切面的后置处理中把全局事务的状态修改为成功。

  • 业务操作中任何一个失败了时抛出异常,捕捉到异常后全局事务标示为失败,返回请求放失败。异步线程通过记录的补偿方法进行事务组向前回滚。


参考资料:


【1】https://www.infoq.cn/article/z4Z1AkW1B3lp1NDw3jyG

【2】https://ask.naixuejiaoyu.com/article/147

【3】https://www.naixuejiaoyu.com/nam.html