38

分布式柔性事务之Saga详解

 3 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzIzODIzNzE0NQ%3D%3D&%3Bmid=2654423540&%3Bidx=1&%3Bsn=da4aef807d327b89c226a2efdd99c829
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

-     起源     -

Saga模型起源于1987年 Hector Garcia-Molina,Kenneth Salem 发表的论文《Sagas》,是分布式事务相关概念最早出现的。

Saga模型是把一个分布式事务拆分为多个本地事务,每个本地事务都有相应的执行模块和补偿模块(对应TCC中的Confirm和Cancel),当Saga事务中任意一个本地事务出错时,可以通过调用相关的补偿方法恢复之前的事务,达到事务最终一致性。

-     组成     -

Saga模型主要分:

  • 一串子事务(本地事务)的事务链

  • 每个Saga子事务Tn, 都有对应的补偿定义 Cn用于撤销Tn造成的结果

  • 每个Tn都没有“预留”动作,直接提交到库。

执行顺序:

  • 子事务序列 T1, T2, …, Tn得以完成 (最佳情况)

  • 或者序列 T1, T2, …, Tj, Cj-1, …, C2, C1, 0 < j < n, 得以完成

数据隔离性:

  • 业务层控制并发

  • 在应用层加锁

  • 应用层预先冻结资源等

恢复方式:

  • 向后恢复:补偿所有已完成的事务,如果任一子事务失败

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

从Saga模型的上述定义中,Saga 模型可以满足事务的三个特性:

  • 原子性:Saga 协调器协调事务链中的本地事务要么全部提交,要么全部回滚。

  • 一致性:Saga 事务可以实现最终一致性。

  • 持久性:基于本地事务,所以这个特性可以很好实现。

从数据隔离性上分析,我们可以发现Saga模型无法保证外部的原子性和隔离性,因为可以查看其他sagas的部分结果,论文中有对应的表述:

bMFv6b6.png!web

-     注意事项     -

Saga 事务和 TCC 事务一样,都是强依靠业务改造,所以要求业务方在设计上要遵循三个策略:

  • 允许空补偿:网络异常导致事务的参与方只收到了补偿操作指令,因为没有执行过正常操作,因此要进行空补偿。

  • 保持幂等性:事务的正向操作和补偿操作都可能被重复触发,因此要保证操作的幂等性。

  • 防止资源悬挂:原因是网络异常导致事务的正向操作指令晚于补偿操作指令到达,则要丢弃本次正常操作,否则会出现资源悬挂问题。

-     Saga和TCC对比     -

虽然 Saga 和 TCC 都是补偿事务,但是由于提交阶段不同,所以两者也是有不同的:

  • Saga 没有Try行为,直接Commit,所以会留下原始事务操作的痕迹,Cancel属于不完美补偿,需要考虑对业务上的影响。TCC Cancel是完美补偿的Rollback,补偿操作会彻底清理之前的原始事务操作,用户是感知不到事务取消之前的状态信息的。

  • Saga 的补偿操作通常可以异步执行,TCC的Cancel和Confirm可以跟进需要是否异步化。

  • Saga 对业务侵入较小,只需要提供一个逆向操作的Cancel即可;而TCC需要对业务进行全局性的流程改造。

  • TCC最少通信次数为2n,而Saga为n(n=子事务的数量)。

-     Saga实现     -

目前业界提供了两类Saga的实现方式,一种是基于业务逻辑层Proxy设计(基于AOP实现),比如华为的ServiceComb; 一种是状态机实现的机制,比如阿里的Seata的Saga模式。

Aop Proxy实现原理如下:

QjaMRvu.png!web

业务逻辑层调用上加上事务注解@Around(“execution(* *(..)) && @annotation(TX)”),Proxy在真正业务逻辑被调用之前, 生成一个全局唯一 TXID 标示事务组,TXID保存在ThreadLocal变量里,方法开始前写入,完成后清除,并向远端数据库写入 TXID 并把事务组置为开始状态。 业务逻辑层调用数据访问层之前,通过RPCProxy代理记录当前调用请求参数。 如果业务正常,调用完成后,当前方法的调用记录存档或删除。 如果业务异常,查询调用链反向补偿

Q7zAFrb.png!web

数据访问层设计: 原始接口必须保证幂等性,满足本地原子性。 提供补偿接口实现反向操 作。 这方面可以在框架层面做一些通用补偿实现,降低使用成本,当然补偿接口也是必须也有幂等性保证。 还可以提供补偿注解,基于原则接口方法,在方法名加注解标注补偿方法名: @Compensable(cancelMethod=“cancelRecord”)

yaiMZj6.png!web

补偿策略:首先是调用执行失败,修改事务组状态;其次分布式事务补偿服务异步执行补偿

状态机引擎Saga原理如下:流程为--先执行stateA, 再执行stateB,然后执行stateC

eqIvueB.png!webeqIvueB.png!web

"状态"的执行是基于事件驱动的模型,stateA执行完成后,会产生路由消息放入EventQueue,事件消费端从EventQueue取出消息,执行stateB。

在整个状态机启动时会调用Seata Server开启分布式事务,并生产xid, 然后记录"状态机实例"启动事件到本地数据库。

当执行到一个"状态"时会调用Seata Server注册分支事务,并生产branchId, 然后记录"状态实例"开始执行事件到本地数据库。

当一个"状态"执行完成后会记录"状态实例"执行结束事件到本地数据库, 然后调用Seata Server上报分支事务的状态。

当整个状态机执行完成, 会记录"状态机实例"执行完成事件到本地数据库, 然后调用Seata Server提交或回滚分布式事务。

-     Saga Aop Proxy流程示例     -

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

6zMvAva.png!web

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

uQNNziz.png!web

-     总结     -

我们已经介绍了XA、2PC、3PC、TCC四种事务模型,但是都不大推荐使用。本文的Saga模式是我主推荐的事务模型,可以适用于大部分的同步事务上。因为华为的ServiceComb中的事务模块目前并非十分独立,所以强烈推荐Seata。Seata不仅支持Saga模式,,还提供了状态机的可视化操作制作,使用成本比较底下。而且Seata的AT模式利用数据库镜像实现了自动补偿机制,又更进一步的优化了Saga模型的缺点。

-     作者介绍    -

孙玄

毕业于浙江大学,奈学教育创始人兼CEO,前转转公司技术委员会主席,前58集团技术委员会主席,前百度资深研发工程师,腾讯云TVP,阿里云MVP,在线直播大课《百万架构师》品牌创始人。

林淮川

毕业于西安交通大学;奈学教育《百万架构师训练营》的讲师,奈学教育企业级源码内源负责人,前大树金融高级架构师;前大树金融技术委员会开创者;前大树金融供应链金融技术总监;前天阳宏业交易事业部技术主管;多年互联网金融行业(ToB)经验。

fU7JNn2.jpg!web

-   关注“架构之美”   -

阅读原文 ”有老师出镜小聊片刻,也欢迎小伙伴关注B站“ 架构师 那点事儿 续还会有更多精彩内容!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK