分布式事务设计与实践-TCC与Saga
分布式事务概述
分布式事务产生背景
传统的单体应用,商品服务,用户服务和交易服务程序都编写在一个单体项目中,通过启动一个JVM进程和连接一个数据库就能提供所有功能,这时就可以通过数据库的ACID来保证事务一致性。
随着业务的发展和技术的发展,衍生出了上图的微服务架构,各个服务都是一个独立的JVM进程,并且都有自己独立的数据库和缓存服务,这时通过数据库本地事务无法保证分布式业务的一致性。
分布式事务分类
1.刚性事务
强一致性:各个业务操作必须在事务结束时全部成功,或者全部失败
XA模型
满足CAP理论的CP
2.柔性事务
保证最终一致性,事务结束后在可接受的时间范围内,可能出现短暂的不一致,最终会达成一致性
满足CAP理论的AP,满足BASE理论
刚性事务
满足传统事务特性
ACID(原子性,一致性,隔离性,持久性)
满足XA模型
应用程序(AP):定义事务边界(事务开始与结束),并且访问事务边界内的资源
资源管理器(RM):管理计算机共享的资源,资源即数据库
事务管理器(TM):负责管理全局事务,分配全局事务ID,监测事务的执行速度,并负责事务的提交,回滚,失败恢复等。
2PC(两阶段提交)是XA规范标准实现
实现过程:
AP发起一个全局事务,并且创建一个全局事务ID
TM发起prepare投票,RM对投票进行表决
RM都同意后,TM发起commit提交。其中任何一个prepare时不同意,TM都会发起rollback。
在commit过程中,发生宕机等异常,在服务重启后根据XA recover再次进行补偿,保证最终commit操作成功。
缺点:
同步阻塞模型
数据库资源锁定时间过程
全局锁(隔离级别串行化),并发能力低
不适合长事务
在一些异常情况下会有问题,在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补偿方式:
向后恢复:补偿所有已完成的事务,本质就是所有已完成的本地事务进行回滚操作
向前恢复:重试失败的事务,假设每个子事务最终都会成功
1.业务逻辑层调用加上事务注解@Around(“execution(* *(..)) && @annotation(TX)”),在业务入口方法加上这个事务注解,表明开启一个全局事务。
2.AOP在真正调用业务逻辑调用之前生成一个全局唯一事务TXID标示事务组,TXID保存在ThreadLocal变量中,方法开始前写入,完成后清除,并把TXID写入到数据库中,并把事务组的状态置为开始状态。
3.业务逻辑层调用数据访问层之前,通过RPCProxy代理记录当前调用请求参数。
4.如果业务正常,调用完成之后,当前方法的调用记录存档或者删除,把全局事务状态标示为成功。如果业务异常,抛出异常,全局事务标示为失败,定时任务进行异步反向补偿。
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