专栏首页分布式架构分布式架构设计篇(九)-柔性事务之Saga详解
原创

分布式架构设计篇(九)-柔性事务之Saga详解

- 起源 -

​ 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的部分结果,论文中有对应的表述:

- 注意事项 -

​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实现原理如下:

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

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

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

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

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

  1. 在整个状态机启动时会调用Seata Server开启分布式事务,并生产xid, 然后记录"状态机实例"启动事件到本地数据库。
  2. 当执行到一个"状态"时会调用Seata Server注册分支事务,并生产branchId, 然后记录"状态实例"开始执行事件到本地数据库。
  3. 当一个"状态"执行完成后会记录"状态实例"执行结束事件到本地数据库, 然后调用Seata Server上报分支事务的状态。
  4. 当整个状态机执行完成, 会记录"状态机实例"执行完成事件到本地数据库, 然后调用Seata Server提交或回滚分布式事务。
- Saga Aop Proxy流程示例 -

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

正常流程

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

异常流程
- 总结 -

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

- 作者介绍 -

林淮川

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

孙玄

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

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 分布式架构设计篇(十一)-柔性事务之最大努力通知事务详解

    ​ 咱们在上一篇文章探讨了事务消息,事务消息是基于MQ实现的一种异步事务。接下来咱们开始聊聊咱们分布式事务系列中的最后一个方案:最大努力通知事务。最...

    林淮川
  • 分布式架构设计篇(七)-刚性事务总结和柔性事务概述

    ​ 在《分布式架构之设计篇-刚性事务之2PC详解》和《分布式架构之设计篇-刚性事务之3PC详解》二文中分析了分布式事务的本质、XA、2PC、3PC等...

    林淮川
  • 分布式架构设计篇(十)-柔性事务之事务消息详解

    ​ 在 《柔性事务之TCC详解》 和《柔性事务之Saga详解》两文中我们详细剖析了柔性事务的第一个分支补偿型事务。在《刚性事务总结和柔性事务概述》中...

    林淮川
  • 「架构技术专题」作为java程序员的你还不知道网站架构的演化(2)?

    CDN和反向代理的基本原理都是缓存,区别在于CDN部署在网络提供商的机房,而反向代理是部署在网站的中心机房,当用户请求到达中心机房后,首先访问的反向代理,如果反...

    java进阶架构师
  • Mysql各版本 - 从库多线程执行 relay log

    在支持 并行复制的 Mysql 版本中,从库中负责执行 relay log 的 线程 sql_thread 被分成

    执生
  • 怎么理解int main(int argc, const char *argv[])

    每次创建一个程序总会看到已经编写好的Hello World程序(如下代码 0-1):

    莫斯
  • PostgreSQL中删除的数据能否恢复

    问题的提出 有人问PostgreSQL数据库中刚刚删除的数据能否被恢复? 或更进一步,如果如要在一个事务中做了一系列的更新、删除、插入的操作后,把这个事务提交之...

    沃趣科技
  • MySQL基础篇3 mysql的事务隔离

    MySQL 是一个支持多引擎的系统,但并不是所有的引擎都支持事务。比如 MySQL 原生的 MyISAM 引擎就不支持事务,这也是 MyISAM 被 InnoD...

    历久尝新
  • Linux块层多队列之引入内核

    Linux块设备多队列机制在Linux3.13中引入,刚开始引入多队列时是多队列和单队列并存。

    jeff xie
  • 设计模式—–里氏替换原则

    开放封闭原则(Open Closed Principle)是构建可维护性和可重用性代码的基础。它强调设计良好的代码可以不通过修改而扩展,新的功能通过添加新的代码...

    对弈

扫码关注云+社区

领取腾讯云代金券