saga分布式事务_分布式事务原理

saga分布式事务_分布式事务原理saga是分布式事务领域里一个非常重要的事务模式,特别适合解决出行订票这类的长事务,本文将深度剖析saga事务的设计原理,以及在解决订票问题上的最佳实践01.saga的理论来源saga这种事务模式最早来自这篇论文:sagas在这篇论文里,作者提出了将一个长事务,分拆成多个子事务,每个子事务有正向操作Ti,反向补偿操作Ci。假如所有的子事务Ti依次成功完成,全局事务完成假如子事务Ti失败,那么会调用Ci,Ci-1,Ci-2….进行补偿论文阐述了上述这部分基本的saga逻辑之后

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

saga是分布式事务领域里一个非常重要的事务模式,特别适合解决出行订票这类的长事务,本文将深度剖析saga事务的设计原理,以及在解决订票问题上的最佳实践

01.saga的理论来源

saga这种事务模式最早来自这篇论文:sagas

在这篇论文里,作者提出了将一个长事务,分拆成多个子事务,每个子事务有正向操作Ti,反向补偿操作Ci。

假如所有的子事务Ti依次成功完成,全局事务完成

假如子事务Ti失败,那么会调用Ci, Ci-1, Ci-2 ….进行补偿

论文阐述了上述这部分基本的saga逻辑之后,提出了下面几种场景的技术处理

回滚与重试

对于一个SAGA事务,如果执行过程中遭遇失败,那么接下来有两种选择,一种是进行回滚,另一种是重试继续。

回滚的机制相对简单一些,只需要在进行下一步之前,把下一步的操作记录到保存点就可以了。一旦出现问题,那么从保存点处开始回滚,反向执行所有的补偿操作即可。

假如有一个持续了一天的长事务,被服务器重启这类临时失败中断后,此时如果只能进行回滚,那么业务是难以接受的。 此时最好的策略是在保存点处重试并让事务继续,直到事务完成。

往前重试的支持,需要把全局事务的所有子事务事先编排好并保存,然后在失败时,重新读取未完成的进度,并重试继续执行。

并发执行

对于长事务而言,并发执行的特性也是至关重要的,一个串行耗时一天的长事务,在并行的支持下,可能半天就完成了,这对业务的帮助很大。

某些场景下并发执行子事务,是业务必须的要求,例如订多张及票,而机票确认时间较长时,不应当等前一个票已经确认之后,再去定下一张票,这样会导致订票成功率大幅下降。

在子事务并发执行的场景下,支持回滚与重试,挑战会更大,涉及了较复杂的保存点。

02.saga的实现分类

目前看到市面上已经有很多的saga实现,他们都具备saga的基本功能。

这些实现,可以大致可以分为两类

状态机实现

这一类的典型实现有seata的saga,他引入了一个DSL语言定义的状态机,允许用户做以下操作:

  • 在某一个子事务结束后,根据这个子事务的结果,决定下一步做什么

  • 能够把子事务执行的结果保存到状态机,并在后续的子事务中作为输入

  • 允许没有依赖的子事务之间并发执行

这种方式的优点是:

  • 功能强大,事务可以灵活自定义

缺点是:

  • 状态机的使用门槛非常高,需要了解相关DSL,可读性差,出问题难调试。官方例子是一个包含两个子事务的全局事务,Json格式的状态机定义大约有95行,较难入门。

  • 接口入侵强,只能使用特定的输入输出接口参数类型,在云原生时代,对强类型的gRPC不友好(gRPC协议,在TM拿不到用户自定义的输入输出pb文件,因此无法解析结果中的字段

非状态机实现

这一类的实现有eventuate的saga,dtm的saga。

在这一类的实现中,没有引入新的DSL来实现状态机,而是采用函数接口的方式,定义全局事务下的各个分支事务:

优点:

  • 简单易上手,易维护

缺点:

  • 难以做到状态机的事务灵活自定义

03.dtm的saga设计

dtm支持TCC和saga模式,这两个模式有不同的特点,各自适应不同的业务场景,相互补充。

saga分布式事务_分布式事务原理

上述这张表,很好的比较了TCC和SAGA这两种事务模式。

TCC的定位是一致性要求较高的短事务。一致性要求较高的事务一般都是短事务(一个事务长时间未完成,在用户看来一致性是比较差的,一般没有必要采用TCC这种高一致性的设计),因此TCC的事务分支编排放在了AP端(即程序代码里),由用户灵活调用。这样用户可以根据每个分支的结果,做灵活的判断与执行。

SAGA的定位是一致性要求较低的长事务/短事务。对于类似订机票这种这样的场景,持续时间长,可能持续几分钟到一两天,就需要把整个事务的编排保存到服务器,避免发起全局事务的APP因为升级、故障等原因,导致事务编排信息丢失。

状态机提供的灵活性对于在客户端编排的TCC是没必要的,但是对于保存在服务器端的saga是有意义的。我在最初设计saga的时候,进行了较详细的权衡取舍。状态机的这种方式,上手难度非常高,用户容易望而却步。我找了一些用户做需求调研,总结出来的核心需求有:

  • 子事务并发执行,降低延时。例如旅游订票业务的预定往返机票,因为订票可能需要较长时间才能够确认,等去的机票定好之后再订返程票,容易导致订不上。

  • 有些操作无法回滚,需要放在可回滚的子事务之后,保证一旦执行,就能够最终成功。

在这两项核心需求下,dtm的saga最终没有采用状态机,但是支持了子事务的并发执行以及指定子事务之间的顺序关系。

下面我们以一个实际问题作为例子,讲解dtm中saga的用法

对于订票类业务,子事务的执行结果不是立即返回的,通常是预定机票后,过一段时间第三方才通知结果。对于这种情况dtm的saga提供了良好的支持,它支持子事务返回进行中的结果,并支持指定重试时间间隔。订票的子事务可以在自己的逻辑中,如果未下订单,则下订单;如果已下订单,那么此时就是重试的请求,可以去第三方查询结果,最后返回成功/失败/进行中。

04.解决问题实例

我们以一个真实用户案例,来讲解dtm的saga最佳实践。

问题场景:一个用户出行旅游的应用,收到一个用户出行计划,需要预定去三亚的机票,三亚的酒店,返程的机票。

要求:

  1. 两张机票和酒店要么都预定成功,要么都回滚(酒店和航空公司提供了相关的回滚接口)

  2. 预订机票和酒店是并发的,避免串行的情况下,因为某一个预定最后确认时间晚,导致其他的预定错过时间

  3. 预定结果的确认时间可能从1分钟到1天不等

上述这些要求,正是saga事务模式要解决的问题,我们来看看dtm怎么解决(以Go语言为例)。

首先我们根据要求1,创建一个saga事务,这个saga包含三个分支,分别是,预定去三亚机票,预定酒店,预定返程机票

  saga := dtmcli.NewSaga(DtmServer, gid).
        Add(Busi+"/BookTicket", Busi+"/BookTicketRevert", bookTicketInfo1).
        Add(Busi+"/BookHotel", Busi+"/BookHotelRevert", bookHotelInfo2).
        Add(Busi+"/BookTicket", Busi+"/BookTicketRevert", bookTicketBackInfo3)

然后我们根据要求2,让saga并发执行(默认是顺序执行)

  saga.EnableConcurrent()

最后我们处理3里面的“预定结果的确认时间”不是即时响应的问题。由于不是即时响应,所以我们不能够让预定操作等待第三方的结果,而是提交预定请求后,就立即返回状态-进行中。我们的分支事务未完成,dtm会重试我们的事务分支,我们把重试间隔指定为1分钟。

代码

05.高级用法

在实际应用中,还遇见过一些业务场景,需要一些额外的技巧进行处理

支持重试与回滚

dtm要求业务明确返回以下几个值:

  • SUCCESS表示分支成功,可以进行下一步

  • FAILURE 表示分支失败,全局事务失败,需要回滚

  • ONGOING表示进行中,后续按照正常的间隔进行重试

  • 其他表示系统问题,后续按照指数退避算法进行重试

部分第三方操作无法回滚

例如一个订单中的发货,一旦给出了发货指令,那么涉及线下相关操作,那么很难直接回滚。对于涉及这类情况的saga如何处理呢?

我们把一个事务中的操作分为可回滚的操作,以及不可回滚的操作。那么把可回滚的操作放到前面,把不可回滚的操作放在后面执行,那么就可以解决这类问题

 saga := dtmcli.NewSaga(DtmServer, dtmcli.MustGenGid(DtmServer)).
       Add(Busi+"/CanRollback1", Busi+"/CanRollback1Revert", req).
       Add(Busi+"/CanRollback2", Busi+"/CanRollback2Revert", req).
       Add(Busi+"/UnRollback1", Busi+"/UnRollback1NoRevert", req).
       EnableConcurrent().
       AddBranchOrder(2, []int{0, 1}) // 指定step 2,需要在0,1完成后执行

超时回滚

saga属于长事务,因此持续的时间跨度很大,可能是100ms到1天,因此saga没有默认的超时时间。

dtm支持saga事务单独指定超时时间,到了超时时间,全局事务就会回滚。

  saga.SetOptions(&dtmcli.TransOptions{TimeoutToFail: 1800})

在saga事务中,设置超时时间一定要注意,这类事务里不能够包含无法回滚的事务分支,否则超时回滚这类的分支会有问题。

其他分支的结果作为输入

如果极少数的实际业务不仅需要知道某些事务分支是否执行成功,还想要获得成功的详细结果数据,那么dtm如何处理这样的需求呢?例如B分支需要A分支的执行成功返回的详细数据。

dtm的建议做法是,在ServiceA再提供一个接口,让B可以获取到相关的数据。这种方案虽然效率稍低,但是易理解已维护,开发工作量也不会太大。

PS:有个小细节请注意,尽量在你的事务外部进行网络请求,避免事务时间跨度变长,导致并发问题。

小结

本文总结了saga相关的理论知识、设计原则,对比了saga的不同实现及其优缺点。最后以一个现实中的问题案例,详细讲解dtm的saga事务使用

dtm是一个一站式的分布式事务解决方案,支持事务消息、SAGA、TCC、XA等多种事务模式,支持Go、Java、Python、PHP、C#、Node等语言SDK。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/193622.html原文链接:https://javaforall.net

(0)
上一篇 2025年11月21日 下午9:22
下一篇 2025年11月21日 下午10:01


相关推荐

  • 连接linux桌面命令,连接Linux远程桌面的四个方法

    连接linux桌面命令,连接Linux远程桌面的四个方法有下面四种方法 一 通过 xshell 直接调用程序窗口 二 通过 xbrower 连接 Linux 完整的桌面 三 通过 Linux 连接 Linux 的远程桌面 可完整桌面 也可打开程序窗口 四 通过 VNC 连接桌面 一 安装 xmanager 用 xshell 连接到 Linux 服务器 直接在命令里输入就可以 比如输入 system config network 可在 windows 这边直接看到网络配置窗口 二 通过 xshe

    2026年3月16日
    2
  • mysql和sqlyog安装教程_mysql 全连接

    mysql和sqlyog安装教程_mysql 全连接最近在学java,然后有涉及数据库,老师说是用MySQL,之前学数据库的时候用的是OracleDatabaseExpress11g,不一样,又得搞一次安装。看了很多教程,也踩了很多坑,记录一下。1.下载MySQLInstaller我下载的是MSIInstaller,感觉这个比较快。也可以下载ZIP,看了教程说要添加my.ini文件,改环境变量什么的。好麻烦。看了用MSIInstaller安装的,不用,所以。。MSIInstaller下载链接选择第二个下载选择Nothanks,j

    2025年6月12日
    6
  • Activiti7工作流+SpringBoot

    Activiti7工作流+SpringBoot一 Activiti 介绍 Activiti 是基于 Apache 许可的开源 BPM 平台 创始人 TomBaeyens 原是 JBPM 架构师 可以理解为与 JBPM 出自同一祖师爷 它提供了 Eclipse 插件 开发可以通过插件直接绘制业务流程图 基于 Spring ibatis 等框架 并在此之上构建了非常清晰的开发框架 是由 Alfresco 软件发布的业务流程管理 BPM 框架 它是覆盖了业务流程管理 工作流 服务

    2026年3月26日
    2
  • STM32CubeMX使用方法及功能介绍

    STM32CubeMX使用方法及功能介绍推荐分享一个大神的人工智能教程。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到人工智能的队伍中来!http://www.captainbed.net/strongerhuang我的网站:https://www.strongerhuang.com我的知乎:https://www.zhihu.com/people/strongerHuang.comⅠ、写在前面…

    2022年6月10日
    36
  • Vue 之 使用qrcodejs2生成二维码

    Vue 之 使用qrcodejs2生成二维码1 安装 qrcodejs2npm 在页面处使用 HTML 作为二维码的载体 divid qrcode class qrcode JS 引用 importQRCode qrcodejs2 exportdefaul data return linkUrl 用来画图的链接地址 divid qrcode class qrcode

    2026年3月19日
    2
  • OSSIM安装

    OSSIM安装https blog 51cto com chenguang

    2026年3月19日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号