Alibaba Seata

张开发
2026/4/15 6:48:18 15 分钟阅读

分享文章

Alibaba Seata
1.Seata介绍Seata 是⼀款开源的分布式事务解决⽅案,致⼒于提供⾼性能和简单易⽤的分布式事务服务. Seata 将为 ⽤⼾提供了 AT、TCC、SAGA 和 XA事务模式, 为用户打造了⼀站式的分布式解决⽅案.Seata官网介绍2. 什么是分布式事务2.1 回顾事务学习数据库的时候接触过事务就是把一组sql语句打包成一个整体一起执行要么所有都执行成功即成功如果有一个sql失败那么这一组sql整体就失败。事务必须满⾜ACID特性: Atomicity (原⼦性), Consistency (⼀致性), Isolation (隔离性) 和 Durability (持久性)。• Atomicity (原⼦性)⼀个事务中的所有操作, 要么全部成功, 要么全部失败, 不会出现只执⾏了 ⼀半的情况, 如果事务在执⾏过程中发⽣错误, 会回滚 ( Rollback ) 到事务开始前的状态, 就像这个事务从来没有执⾏过⼀样.• Consistency (⼀致性)在事务开始之前和事务结束以后, 数据库的完整性不会被破坏. 这表⽰ 写⼊的数据必须完全符合所有的预设规则, 包括数据的精度、关联性以及关于事务执⾏过程中服务 器崩溃后如何恢复.• Isolation (隔离性)数据库允许多个并发事务同时对数据进⾏读写和修改, 隔离性可以防⽌多 个事务并发执⾏时由于交叉执⾏⽽导致数据的不⼀致. 事务可以指定不同的隔离级别读未提交读已提交不可重复读序列化, 以权衡在不同 的应⽤场景下数据库性能和安全• Durability (持久性)事务处理结束后, 对数据的修改将永久的写⼊存储介质, 即便系统故障也 不会丢失.以上是针对单库多表的情况事务所要满⾜的特性2.2 分布式事务在微服务架构下, 随着业务服务的拆分及数据库的拆分会存在如下图所⽰的场景, 订单和库存分别拆分 成了两个独⽴的数据库, 当客⼾端发起⼀个下单操作时, 需要在订单服务对应的数据库中创建订单, 同时 需要调⽤库存服务完成商品库存的扣减.分布式事务是指在分布式系统中, 为了保证数据的⼀致性和完整性, 对多个节点上的数据进⾏操作的事 务. 当⼀个事务涉及到多个不同的数据库、服务或应⽤实例时, 就构成了分布式事务.3. 分布式事务问题演⽰3.1 项⽬搭建上图中项⽬架构有些复杂, 我们稍作修改, 也可以演⽰分布式事务的问题. ⽤⼾下订单后, 调⽤库存服务扣减库存和⽤⼾服务扣减余额.3.2 问题演⽰观察数据库表此时库存表中编号2001的商品库存为150我们创建一个请求购买2001商品1000个库存不足的情况下money修改为500现在这个情况不符合事务的原子性。4. 分布式事务问题的理论模型分布式事务问题也叫分布式数据⼀致性问题. 简单来说就是如何在分布式场景中保证多个节点数据的⼀ 致性. 分布式事务产⽣的核⼼原因在于存储资源的分布性, ⽐如多个数据库, 或者MySQL和Redis两种不 同存储设备的数据⼀致性等. 在实际应⽤中, 我们应该尽可能地从设计层⾯去避免分布式事务的问题, 因 为任何⼀种解决⽅案都会增加系统的复杂度. 接下来我们了解⼀下分布式事务问题的常⻅解决⽅案. 在学习分布式事务解决⽅案之前, 我们需要先了解⼀些相关的基础理论4.1 CAP理论CAP 理论是分布式系统设计中最基础, 也是最为关键的理论。•⼀致性(Consistency) CAP理论中的⼀致性, 指的是强⼀致性. 所有节点在同⼀时间具有相同的数据•可⽤性(Availability) 保证每个请求都有响应(响应结果可能不对)。•分区容错性(Partition Tolerance) 当出现⽹络分区后, 系统仍然能够对外提供服务。CAP 理论告诉我们: ⼀个分布式系统不可能同时满⾜数据⼀致性, 服务可⽤性和分区容错性这三个基本 需求, 最多只能同时满⾜其中的两个.在分布式系统中, 系统间的⽹络不能100%保证健康, 服务⼜必须对外保证服务. 因此Partition Tolerance不可避免. 那就只能在C和A中选择⼀个. 也就是CP或者AP架构CP架构: 为了保证分布式系统对外的数据⼀致性, 于是选择不返回任何数据AP架构: 为了保证分布式系统的可⽤性, 节点2返回V0版本的数据(即使这个数据不正确)4.2 BASE理论BASE理论是由于CAP中⼀致性和可⽤性不可兼得⽽衍⽣出来的⼀种新的思想, BASE理论的核⼼思想是 通过牺牲数据的强⼀致性来获得⾼可⽤性.它有如下三个特性:• Basically Available (基本可⽤): 分布式系统在出现故障时, 允许损失⼀部分功能的可⽤性, 保证核⼼功能的可⽤• Soft state (软状态): 允许系统中的数据存在中间状态, 也就是允许系统中不同节点的数据副本 之间的同步存在延时, 这个状态不影响系统的可⽤性• Eventually Consistent (最终⼀致性): 中间状态的数据在经过⼀段时间之后, 会达到⼀个最 终的数据⼀致性BASE理论不要求数据的强⼀致, ⽽是允许数据在⼀段时间内是不⼀致的, 但是数据最终会在某个时间点 实现⼀致. 在互联⽹产品中, ⼤部分都会采⽤BASE理论来实现数据的⼀致, 因为产品的可⽤性对于用户来说更加重要.与CAP理论的对⽐CAP理论指出: ⼀个分布式系统不可能同时满⾜⼀致性 (C ) 、可⽤性 (A ) 和分区容错性 (P ) 这三个特性. BASE理论则是CAP理论的补充, 通过放宽对⼀致性的严格要求, 换取系统更⾼的可⽤性和灵活性. BASE理论的核⼼思想是: 如果不是必须的话, 不推荐使⽤事务或强⼀致性, ⿎励可⽤性和性能优先. 允许 在牺牲⼀定⼀致性的前提下获得更⾼的可⽤性.4.3 X/Open 分布式事务模型X/Open 是⼀个组织, X/Open DTP ( Distributed Transaction Process Reference Model) 是X/Open这个组织定义的⼀套分布式事务的标准. 这个标准提出了使⽤两阶段提交(2PC,Two-Phase-Commit) 来保证分布式事务的完整性.这套标准主要定义了实现分布式事务的规范和API, 具体的实现则交给相应的⼚商来实现相关⽂档:DTP 参考模型:https://pubs.opengroup.org/onlinepubs/9294999599/toc.pdfX/Open DTP参考模型包含三种⻆⾊:• AP: Application, 应⽤程序.• RM: Resource Manager, 资源管理器, ⽐如数据库. 应⽤程序可以通过资源管理器对相应的资源进⾏ 有效的控制• TM: Transaction Manager, 事务管理器, ⼀般指事务协调者, 负责协调和管理各个⼦事务, 可以理解 为管理RM.在分布式系统中, 会有多个节点, 每⼀个节点都能够明确的知道⾃⼰在进⾏事务操作过程中的结果是 成功或失败, 但⽆法直接获取到其他分布式节点的操作结果, 因此, 当⼀个事务操作需要跨越多个分布 式节点的时候, 为了保证事务处理的ACID特性, 就需要引⼊⼀个协调者的组件来统⼀调度所有分布 式节点的执⾏逻辑, 这些被调度的节点则称为参与者, 协调者负责调度参与者的⾏为, 并最终决定这 些参与者是否要把事务真正进⾏提交. TM就是协调者, RM就是参与者上图简单介绍1. 应⽤程序(AP) 通过资源管理器操作多个资源.2. 应⽤程序通过TM提供的接⼝, 定义事务边界3. TM和RM交换事务信息(执⾏成功或失败).TM和RM之间的事务控制, 是基于XA协议来完成的, 并且提出了分布式事务的规范⸺XA协议, 该协议 主要定义了 (全局 ) 事务管理器和 (局部 ) 资源管理器之间的接⼝X/Open DTP模型的执⾏流程:1. 配置TM, 把多个RM注册到TM2. AP从TM管理的RM中获取连接, ⽐如JDBC连接3. AP向TM发起⼀个全局事务, ⽣成全局事务ID(XID), XID会通知各个RM4. AP通过第⼆步获得的连接直接操作RM完成数据操作. AP在每次操作时会把XID传递给RM5. AP结束全局事务, TM会通知各个RM全局事务结束. 根据各个RM的事务执⾏结果, 执⾏提交或者回滚 操作4.4 两阶段提交X/Open DTP 标准提出了使⽤两阶段提交(2PC, Two-Phase-Commit) 来保证分布式事务的完整性, 在上图中也有体现. TM对多个RM事务的管理, 就会涉及两个阶段的提交. 第⼀个阶段是事务的准备阶段, 第⼆个是事务的提交或者回滚阶段.1. 准备阶段 (Prepare Phase )◦ 协调者发送准备请求: 协调者向所有参与者发送 prepare 请求, 询问它们是否准备好提交事务. 这个请求包含了事务的详细信息,要求参与者对事务进⾏预处理,并准备好回滚或提交事务所需 的所有资源.◦ 参与者响应准备请求: 参与者在收到 prepare 请求后, 会执⾏事务操作, 但不提交. 如果参与者 成功执⾏了事务操作, 它会将事务的执⾏结果和准备状态记录在本地⽇志中, 并向协调者发送 ready 消息, 表⽰已经准备好提交事务 .如果执⾏失败或⽆法准备, 则向协调者发送 abort 消 息.2. 提交阶段 (Commit Phase )◦ 协调者根据准备阶段的反馈进⾏决策协调者收到所有参与者的响应后, 会根据反馈结果做出决 策. 如果所有参与者都返回 ready , 则协调者决定提交事务. 如果有任何⼀个参与者返回 abort , 则协调者决定回滚事务.◦ 协调者发送提交或回滚请求▪ 提交事务如果协调者决定提交事务, 它会向所有参与者发送 commit 请求. 参与者在收到 commit 请求后, 会正式提交事务, 并释放所有资源, 然后向协调者发送 ack 消息, 表⽰事务 已成功提交.▪ 回滚事务如果协调者决定回滚事务, 它会向所有参与者发送 rollback 请求. 参与者在收 到 rollback 请求后, 会回滚事务, 并释放所有资源, 然后向协调者发送 ack 消息, 表⽰事 务已成功回滚.两阶段提交把⼀个事务的处理过程分为准备和提交/回滚两个阶段, 采⽤简单的⽅式来解决分布式事务 的问题, 但是这个过程中, 存在以下缺点:1. 阻塞问题: 两个阶段都是事务阻塞型的, 对于每⼀个指令都需要有明确的响应, 如果在这个过程中, TM宕机或者⽹络出现故障, 则会⼀直处于阻塞状态. ⽐如第⼀阶段完成后TM宕机或⽹络出现故障了, 此时RM会⼀直阻塞, ⽆法进⾏其他操作. 所以3PC针对此问题, 加⼊了timeout机制.2. 资源占⽤: 参与者在收到准备请求后, 会锁定相关资源以保证事务的原⼦性. 在整个两阶段提交过程 中, 这些资源⼀直被锁定, 直到事务提交或回滚完成. 这会导致资源利⽤率降低, 其他事务可能因⽆法 获取所需资源⽽等待3. 数据不⼀致: 第⼆阶段中, TM向所有的RM发送commit请求, 由于局部⽹络异常, 导致只有⼀部分RM 收到了commit请求, 这些RM节点执⾏commit操作, 没有收到commit请求的节点由于事务⽆法提 交, 出现数据不⼀致的情况相应也存在以下优点• 保证事务的原⼦性2PC通过两个阶段的严格控制, 确保了事务要么全部提交, 要么全部回滚, 从⽽保 证了事务的原⼦性.• 实现相对简单相⽐于其他分布式事务协议, 2PC的实现相对简单, 易于理解和实现4.5 三阶段提交3PC(Three-Phase-Commit), 是2PC的改进版本, 共分为 CanCommit , PreCommit 和 DoCommit 三个阶段.1. CanCommit阶段◦ 协调者发起请求: 协调者向所有参与者发送 CanCommit 请求, 询问它们是否可以执⾏事务提交 操作.此阶段不涉及实际的数据修改, 只是确认每个参与者是否有⾜够的资源和条件来完成事务.◦ 参与者响应: 参与者根据⾃⾝情况返回Yes或No. 如果所有参与者都返回Yes, 则进⼊PreCommit 阶段.2. PreCommit阶段◦ 协调者发送PreCommit请求: 协调者向所有参与者发送 PreCommit 请求, 询问是否可以进⾏事 务的预提交操作.◦ 参与者准备事务: 参与者执⾏事务操作, 并将事务执⾏结果和准备状态 (Yes/No ) 发送给协调者. 参与者会记录预提交⽇志, 并确保这些⽇志是持久化的.◦ 协调者收集反馈并决策: 如果所有参与者都返回Yes, 则进⼊DoCommit阶段. 如果有任何⼀个参 与者返回No或超时未响应, 协调者会发送 abort 请求, 通知所有参与者回滚事务.3. DoCommit阶段◦ 协调者发送DoCommit请求: 协调者向所有参与者发送 DoCommit 请求, 指⽰它们正式提交事 务.◦ 参与者执⾏提交: 参与者收到 DoCommit 请求后, 执⾏事务提交操作, 并向协调者发送 Ack 消 息, 表⽰事务已提交.◦ 超时机制: 如果参与者在等待 DoCommit 请求时超时, 会默认执⾏提交操作.优点• 减少阻塞: 3PC通过引⼊超时机制, 减少了2PC中的阻塞问题. 避免了资源被永久锁定.• 增强容错能⼒: 即使协调者在DoCommit阶段之前出现故障, 参与者也可以基于其预提交的状态⾃主决定继续提交或回滚事务, 从⽽减少了对协调者的依赖.缺点• 实现复杂度⾼3PC的实现⽐2PC更复杂, 增加了系统的开发和维护成本• 数据不⼀致⻛险在某些情况下, 如⽹络分区, 参与者在收到 PreCommit 消息后, 如果⽹络出现故 障, 协调者和参与者⽆法进⾏后续通信, 参与者在超时后可能会⾃⾏提交事务, 导致数据不⼀致.4.6 TCC事务TCC (Try-Confirm-Cancel ) 是⼀种分布式事务解决⽅案, 是由Pat Helland在2007年发表的论⽂《Life beyond Distributed Transactions: An Apostate’s Opinion》中提出. TCC事务相对于传统两阶段, 其 特征在于它不依赖资源管理器(RM)对XA的⽀持, ⽽是通过对 (由业务系统提供的 ) 业务逻辑的接⼝调⽤ 来实现分布式事务.TCC 通过将事务操作拆分为三个阶段:1. Try阶段尝试执⾏业务操作, 完成所有业务检查, 并预留必要的业务资源. 这个阶段不真正执⾏事 务, 只是进⾏资源的预占.2. Confirm阶段: 如果所有参与者在Try阶段都成功, 那么进⼊Confirm阶段, 正式完成操作, 使⽤之前预 留的资源.3. Cancel阶段如果任何⼀个参与者在Try阶段失败, 那么进⼊Cancel阶段, 所有参与者回滚在Try阶 段执⾏的操作, 释放预留的资源TCC事务属于两阶段提交思想的变体, 它在设计上借鉴了两阶段提交的核⼼理念, 第⼀阶段通过Try进⾏ 准备⼯作, 第⼆阶段Confirm/Cancel表⽰Try阶段操作的确认和回滚.在主业务⽅法中, 会先调⽤业务服务对外提供的 Try ⽅法来做资源预留, 如果业务服务 Try ⽅法处理 都正常, TCC事务协调器就会调⽤ Confirm ⽅法对预留资源进⾏实际应⽤. 否则就会调⽤各个服务的 Cancel ⽅法进⾏回滚, 从⽽保证数据的⼀致性.优点• ⽆需依赖第三⽅中间件或数据库来实现分布式事务, 降低了系统复杂度和成本• ⽆需锁定全局资源, 提⾼了系统的并发性能和可⽤性• 适⽤于各种类型的业务场景, 只要能够定义出清晰的Try、Confirm和Cancel逻辑缺点• 需要开发⼈员⼿动编写三个阶段的业务逻辑, 并保证其正确性和⼀致性, 增加了开发难度和维护成本• 需要考虑各种异常情况和边界情况, 并提供相应的补偿策略和重试机制, 增加了系统复杂度和⻛险5. 初识Seata5.1 Seata术语介绍• TC (Transaction Coordinator) - 事务协调者维护全局和分⽀事务的状态, 驱动全局事务提交或回滚.• TM (Transaction Manager) - 事务管理器定义全局事务的范围开始全局事务、提交或回滚全局事务.• RM (Resource Manager) - 资源管理器管理分⽀事务处理的资源, 与TC交谈以注册分⽀事务和报告分⽀事务的状态, 并驱动分⽀事务提交或 回滚5.2 Seata 下载和部署1. Seata下载解压下载地址https://seata.apache.org/zh-cn/download/seata-server/下载后解压得到以下文件• seata-namingserver: Seata 原⽣的注册中⼼操作可参考https://seata.apache.org/zh-cn/docs/user/registry/namingserver/• seata-server : Seata 的事务协调服务端, 负责全局事务的协调和管理bin是运行脚本• seata-server.sh: Linux系统下启动Seata服务的脚本. 通过执⾏该脚本并传⼊相应参数, 即可启动 Seata服务. 如 sh ./bin/seata-server.sh -m nio -p 8091• seata-server.batWindows系统下启动Seata服务的脚本. 双击即可启动Seata服务.conf: 配置⽂件• ⽤于配置Seata服务的⼀些⾼级参数如存储模式、数据库连接信息等lib: 依赖库script: ⼀些脚本⽂件.⽐如与配置中⼼相关的脚本, 数据库相关的脚本, 如建表语句等.2. 修改配置Seata⽀持多种配置中⼼• nacos • consul • apollo • etcd • zookeeper • file (读本地⽂件, 包含conf、properties、yml配置⽂件的⽀持)此处我们使⽤Nacos来作为Seata的配置中⼼1修改配置中心2修改注册中心同上3. 修改Seata存储模式Server端存储模式(store.mode) ⽀持file, db, redis, raft• file模式为单机模式, 全局事务会话信息内存中读写并异步(默认)持久化本地⽂件root.data, 性能较 ⾼;• db模式为⾼可⽤模式, 全局事务会话信息通过db共享, 相应性能差些.如果使⽤file模式, ⽆需改动, 直接启动即可。使用db模式根据地址找到文件mysql文件apache-seata-2.2.0-incubating-bin\seata-server\script\server\db创建一个数据库命名都可以把sql语句在数据库中运行得到以下新建的数据库修改store.modestore: # support: file 、 db 、 redis 、 raft mode: db db: datasource: druid db-type: mysql driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatementstrue user: root password: 123456 min-conn: 10 max-conn: 100 global-table: global_table branch-table: branch_table lock-table: lock_table distributed-lock-table: distributed_lock vgroup-table: vgroup_table query-limit: 1000 max-wait: 50004. 启动Seatawindow系统下直接双击bin目录下的bat文件访问 http://127.0.0.1:7091/, ⽤⼾名密码: seata/seataLinux1.把压缩包直接拖拽到服务器中。2.新建一个文件夹用来存放seata解压好的文件。可以不创建但是有点乱解压后3.对压缩包进行解压这个压缩包解压存储到seata文件中tar zxvf apache-seata-2.2.0-incubating-bin.tar.gz -C seata/4.打开/seata/seata-server/conf这个目录找到application。yml文件修改这个文件中的配置如同windows中配置的一样配置配置中心配置注册中心....这里可以直接把window中配置好的直接拿来用。先用rm application.yml把这个文件删除再把配置文件直接拉进来即可。5通过vi application.yml命令打开配置文件配置nacos配置中心把端口号改成127.0.0.1:10020再配置nacos注册中心把端口号改成127.0.0.1:10020配置数据库把账号改成Linux装上的数据库的账号密码。6打开数据库导入数据。先查询建表的语句再这个目录下/seata/seata-server/script/server/db用pwd打印出当前路径复制路径通过这个命令再数据库中执行导入数据表source /root/seata/seata-server/script/server/db/mysql.sql7启动seata通过这个路径找到启动文件/seata/seata-server/bin通过这个命令启动seata服务指定端口号为8091bash seata-server.sh -p 8091观察nacos启动成功上面的是linux中的服务下面的是window中启动的服务。但是发现Linux中的ip是内网IP也就是局域网IP这样的话就访问不通解决这个问题就需要我们在启动的时候指定ip1先杀掉现在启动的seata服务查找进程ps -ef|grep seata杀掉进程kill 进程号再次启动bash seata-server.sh -h 49.232.195.59 -p 8091通过-h来指定IP地址启动成功5.3 微服务集成Seata5.3.1 引⼊依赖在需要的微服务中引⼊seata依赖.dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-seata/artifactId /dependency5.3.2 修改配置⽂件在application.yml, 添加seata相关配置根据配置信息确定TC服务地址信息seata: registry: #定义了Seata Server的注册中⼼配置, 微服务根据配置信息去注册中⼼获取tc服务地址 type: nacos #指定注册中⼼的类型 nacos: application: seata-server #Seata Server在Nacos中的应⽤名称 server-addr: 49.232.195.59:10020 #Nacos服务器地址 group: SEATA_GROUP #Seata Server在Nacos中的分组名称 namespace: #Nacos的命名空间, 设置为空, 表⽰使⽤默认的命名空间public tx-service-group: default_tx_group #定义事务服务组的名称 service: vgroup-mapping: default_tx_group: default6事务模式6.1 XA模式XA实现的原理是基于两阶段提交.Seata 对原始的XA模式做了简单的封装和改造, 以适应⾃⼰的事务模型, 在 Seata 定义的分布式事务框 架内, 利⽤事务资源 (数据库、消息服务等 ) 对 XA 协议的⽀持, 以 XA 协议的机制来管理分⽀事务的⼀种 事务模式.配置与使用1. 在application.yml中配置seata的事务模式.seata:/>开启服务当输入错误信息的时候没有执行任何数据库操作下订单失败。6.2 AT 模式两阶段提交协议的演变一阶段业务数据和回滚日志记录在同一个本地事务中提交释放本地锁和连接资源。二阶段提交异步化非常快速地完成。回滚通过一阶段的回滚日志进行反向补偿解释上图一阶段中干了5件事情1TM注册全局事务。2TM调用分支。3RM注册分支任务。4RM执行业务真实的提交到了数据库执行的过程中会记录更新快照用来当回滚的时候回滚到没有执行任务之前。5RM执行完毕后报告分支事务的状态。第二阶段当所有的分支任务都执行完。提交成功所有的都而成功了TC会通知RM清理log日志也就是更新快照。完成了整个分布式事务的处理。提交失败如果有任意一个事务没有执行成功那么TC就会通知RM进行回滚RM会根据更新快照进行回滚事务从而实现全局事务的回滚。6写隔离https://seata.apache.org/zh-cn/docs/dev/mode/at-mode配置与使用在微服务关联的数据库创建undo_log 表, 建表SQL参考CREATE TABLE undo_log ( id bigint(20) NOT NULL AUTO_INCREMENT, branch_id bigint(20) NOT NULL, xid varchar(100) NOT NULL, context varchar(128) NOT NULL, rollback_info longblob NOT NULL, log_status int(11) NOT NULL, log_created datetime NOT NULL, log_modified datetime NOT NULL, ext varchar(100) DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY ux_undo_log (xid,branch_id) ) ENGINEInnoDB AUTO_INCREMENT1 DEFAULT CHARSETutf8;seata:cellpadding="1" cellspacing="1" style="width:500px">ATXA实现⽅式通过记录快照日志(undo_log表)来实现回滚的几乎适用于任何的数据库但是要在业务上创建undo_log表依赖数据库对XA协议的⽀持通过XA规范来实现事务的提交和回滚.不需要导入undo_log表但要求数据库对协议的支持⼀致性最终一致性强一致性性能性能较好一阶段就提交了不锁定资源性能较差一阶段锁定资源等二阶段结束的时候才释放资源数据库⽀持适用于所有数据库仅支持有XA协议的数据库代码侵⼊无无使用场景业务对性能要求较高推荐使用AT要求强一致性例如银行对业务要求性能不高6.3 TCC模式TCC模式就是对TCC事务的实现TCC设计了解了TCC模式之后, 我们先来使⽤ Seata TCC 模式实现⼀个分布式事务.业务操作分析接⼊ TCC 前, 业务操作只需要⼀步就能完成, 但是在接⼊ TCC 之后, 需要考虑如何将其分成 2 阶段完成, 把资源的检查和预留放在⼀阶段的 Try 操作中进⾏, 把真正的业务操作的执⾏放在⼆阶段的 Confirm 操 作中进⾏.用仓库存储为例• Try 操作资源的检查和预留.在扣库存场景下, Try 操作要做的事情就是先检查 A 商品库存是否⾜够, 再冻结要扣的 20 个 (预留资源 ) . 此阶段不会发⽣真正的扣库存.• Confirm 操作执⾏真正业务的提交.在扣库存场景下, Confirm 阶段做的事情就是发⽣真正扣库存, 把A商品中已经冻结的 30 个库存扣掉.• Cancel 操作预留资源的是否释放.在扣库存场景下, 扣库存操作取消, Cancel 操作执⾏的任务是释放 Try 操作冻结的 20个库存, 使 A 商品 回到初始状态.并发情况下可以在并发的情况下顺利执行允许空回滚TCC服务RM没有执行try却收到了Cancle的请求就是空回滚。空回滚操作的时候要判断try有没有被执行。防悬挂控制一种情况当执行一阶段的时候事务管理器向资源管理器发送prepare请求请求途中发生拥堵这个时候事务管理器对资源管理器执行了空回滚操作但是空回滚操作后perpare请求发送到了然后执行try操作这就发生了业务悬挂用户在实现 TCC 服务时, 要允许空回滚, 但是要拒绝执⾏空回滚之后 Try 请求, 要避免出现悬挂.解决这种场景就需要有一个日志在执行空回滚的时候看看try有没有执行在执行try的时候看看有没有发生空回滚幂等控制⽆论是⽹络数据包重传, 还是异常事务的补偿执⾏, 都会导致 TCC 服务的 Try、Confirm 或者 Cancel 操 作被重复执⾏⽤⼾在实现 TCC 服务时, 需要考虑幂等控制, 即 Try、Confirm、Cancel 执⾏⼀次和执⾏ 多次的业务结果是⼀样的.seata解决这些问题给出的解决方法TCC 模式中存在的三⼤问题是幂等、悬挂和空回滚. 在 Seata1.5.1 版本中, 增加了⼀张事务控制表 tcc_fence_log,包含事务的 XID 和 BranchID 信息, 来解决这个问题CREATE TABLE IF NOT EXISTS tcc_fence_log ( xid VARCHAR(128) NOT NULL COMMENT global id, branch_id BIGINT NOT NULL COMMENT branch id, action_name VARCHAR(64) NOT NULL COMMENT action name, status TINYINT NOT NULL COMMENT status(tried:1;committed:2;rollbacked:3;suspended:4), gmt_create DATETIME(3) NOT NULL COMMENT create time, gmt_modified DATETIME(3) NOT NULL COMMENT update time, PRIMARY KEY (xid, branch_id), KEY idx_gmt_modified (gmt_modified), KEY idx_status (status) ) ENGINE InnoDB DEFAULT CHARSET utf8mb4;空回滚在 Try ⽅法执⾏时插⼊⼀条记录, 表⽰⼀阶段执⾏了, 执⾏ Cancel ⽅法时读取这条记录, 如果记录不存 在, 说明 Try ⽅法没有执⾏, 以此来避免空回滚• 悬挂在 Rollback 阶段, 如果查询到事务控制表中没有记录, 说明Cancle先于Try执⾏了. 因此插⼊⼀条 status4 状态的记录. 当Try阶段执⾏时, 判断status4 , 则说明有⼆阶段 Cancel 已执⾏, 并返回 false 以阻⽌⼀阶段 Try ⽅法执⾏成功• 幂等在 TCC 事务控制表中增加⼀个记录状态的字段 status, 该字段有 4 个值, 分别为a. tried(1) : 表⽰ Try 阶段已经执⾏过b. committed(2): 表⽰⼆阶段 Commit 已经执⾏完成c. rollbacked(3): 表⽰⼆阶段 Rollback 已经执⾏完成d. suspended(4): 表⽰空回滚/悬挂/中⽌状态.⼆阶段 Confirm/Cancel ⽅法执⾏后, 将状态改为 committed 或 rollbacked 状态. 当重复调⽤⼆阶段 Confirm/Cancel ⽅法时, 判断事务状态即可解决幂等问题TCC实现1添加数据表CREATE TABLE IF NOT EXISTS tcc_fence_log ( xid VARCHAR(128) NOT NULL COMMENT global id, branch_id BIGINT NOT NULL COMMENT branch id, action_name VARCHAR(64) NOT NULL COMMENT action name, status TINYINT NOT NULL COMMENT status(tried:1;committed:2;rollbacked:3;suspended:4), gmt_create DATETIME(3) NOT NULL COMMENT create time, gmt_modified DATETIME(3) NOT NULL COMMENT update time, PRIMARY KEY (xid, branch_id), KEY idx_gmt_modified (gmt_modified), KEY idx_status (status) ) ENGINE InnoDB DEFAULT CHARSET utf8mb4;2添加冻结字段ALTER TABLE storage_tbl ADD COLUMN freeze_count INT(11) unsigned DEFAULT 0 COMMENT 冻结库存;3修改相应的实体类Seata 会把一个 TCC 接口当成一个 Resource也叫 TCC Resource。在业务接口中核心的注解是TwoPhaseBusinessAction表示当前方法使用 TCC 模式管理事务提交并标明了 TryConfirmCancel 三个阶段。name属性给当前事务注册了一个全局唯一的的 TCC bean name。同时 TCC 模式的三个执行阶段分别是Try 阶段预定操作资源Prepare 这一阶段所以执行的方法便是被TwoPhaseBusinessAction所修饰的方法。如示例代码中的prepare方法。Confirm 阶段执行主要业务逻辑Commit 这一阶段使用commitMethod属性所指向的方法来执行Confirm 的工作。Cancel 阶段事务回滚Rollback 这一阶段使用rollbackMethod属性所指向的方法来执行 Cancel 的工作。其次可以在 TCC 模式下使用BusinessActionContext在事务上下文中传递查询参数。如下属性xid全局事务idbranchId分支事务idactionName分支资源idresource idactionContext业务传递的参数可以通过BusinessActionContextParameter来标注需要传递的参数。创建一个服务层的接口package com.bite.storage.service; import io.seata.rm.tcc.api.BusinessActionContext; public interface StorageTccService { boolean prepare(String commodityCode, Integer count); boolean commit(BusinessActionContext actionContext); boolean rollback(BusinessActionContext actionContext); }实现这个接口package com.bite.storage.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.bite.storage.entity.StorageInfo; import com.bite.storage.mapper.StorageMapper; import com.bite.storage.service.StorageTccService; import io.seata.rm.tcc.api.BusinessActionContext; import io.seata.rm.tcc.api.BusinessActionContextParameter; import io.seata.rm.tcc.api.LocalTCC; import io.seata.rm.tcc.api.TwoPhaseBusinessAction; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; Service Slf4j LocalTCC public class StorageTccServiceImpl implements StorageTccService { Autowired private StorageMapper storageMapper; //被该注解标识的方法即为try阶段执行的方法 commit表示的是二阶段执行的方法 rollback表示的是二阶段指定的方法 //useTCCFence true 加上这个配置项目seata就会自动处理空回滚业务悬挂幂等 Override TwoPhaseBusinessAction( name storageDeduct, commitMethod commit, rollbackMethod rollback,useTCCFence true) public boolean prepare(BusinessActionContextParameter(commodityCode)String commodityCode, BusinessActionContextParameter(count)Integer count) { // BusinessActionContextParameter这个注解就是让下面BusinessActionContext actionContext 拿到里面参数的值 log.info(一阶段try执行.......); try { //检查一下库存是否充足 if(cheakCount(commodityCode,count)){ UpdateWrapperStorageInfo updateWrapper new UpdateWrapper(); updateWrapper.lambda().setSql(count count - count) .setSql(freeze_countfreeze_countcount) .eq(StorageInfo::getCommodityCode, commodityCode); storageMapper.update(updateWrapper); return true; }else { throw new RuntimeException(库存不足); } } catch (Exception e) { log.error(扣减库存失败, e:, e); throw new RuntimeException(扣减库存失败!, e); } } Override public boolean commit(BusinessActionContext actionContext) { log.info(二阶段commit执行.......); //进行冻结库存的扣减 String commodityCode(String) actionContext.getActionContext(commodityCode); Integer count(Integer) actionContext.getActionContext(count); UpdateWrapperStorageInfo updateWrapper new UpdateWrapper(); updateWrapper.lambda() .setSql(freeze_countfreeze_count-count) .eq(StorageInfo::getCommodityCode, commodityCode); Integer resultstorageMapper.update(updateWrapper); return result1; } Override public boolean rollback(BusinessActionContext actionContext) { log.info(二阶段rollback执行.......); //释放冻库存恢复原有的库存 String commodityCode(String) actionContext.getActionContext(commodityCode); Integer count(Integer) actionContext.getActionContext(count); UpdateWrapperStorageInfo updateWrapper new UpdateWrapper(); updateWrapper.lambda() .setSql(count count count) .setSql(freeze_countfreeze_count-count) .eq(StorageInfo::getCommodityCode, commodityCode); Integer resultstorageMapper.update(updateWrapper); return result1; } private Boolean cheakCount(String commodityCode, Integer count) { QueryWrapperStorageInfo queryWrappernew QueryWrapper(); queryWrapper.select().lambda().eq(StorageInfo::getCommodityCode, commodityCode); StorageInfo storageInfo storageMapper.selectOne(queryWrapper); Integer count1 storageInfo.getCount(); if(countcount1){ return false; } return true; } }在controller中调用这个服务6.4Saga模式Saga模式是seata提供的长事务解决方案在业务流程中每一个参与者都提交本地事务当出现某一个参与者失败则补偿前面已经成功的参与者一阶段正向服务和二阶段补偿服务都由业务开发实现。使用场景业务流程长业务流程多参与者包含其他公司或遗留服务无法提供tcc模式要求的三个接口就是一些老的服务不能进行修改和维护的同时没有任何分布式事务引入。ATXATccSAGA实现⽅式通过记录快照日志(undo_log表)来实现回滚的几乎适用于任何的数据库但是要在业务上创建undo_log表依赖数据库对XA协议的⽀持通过XA规范来实现事务的提交和回滚.不需要导入undo_log表但要求数据库对协议的支持开发人员手动实现try,confirm,Cancel事件驱动每个事务包含正向操作和逆向补偿操作失败时按顺序执行逆向补偿⼀致性最终一致性强一致性最终一致性通过业务实现最终一致性性能性能较好一阶段就提交了不锁定资源性能较差一阶段锁定资源等二阶段结束的时候才释放资源较高但开成本高需要处理空回滚业务悬挂等高无锁适合常事务数据库⽀持适用于所有数据库仅支持有XA协议的数据库不依赖底层数据库事务机制不依赖底层数据的事务机制代码侵⼊无无有需要手动编写三个接口有需要手动编写状态机和补偿业务使用场景业务对性能要求较高推荐使用AT要求强一致性例如银行对业务要求性能不高高并发的场景资金/库存业务流程长业务流程多参与者包含其他公司或遗留服务无法提供tcc模式要求的三个接口就是一些老的服务不能进行修改和维护的同时没有任何分布式事务引入

更多文章