一.分布式事务
1.介绍
在微服务架构下,随着业务服务的拆分及数据库的拆分会存在如下图所示的场景,订单和库存分别拆分成了两个独立的数据库,当客户端发起一个下单操作时,需要在订单服务对应的数据库中创建订单,同时需要调用库存服务完成商品库存的扣减。

分布式事务 是指在分布式系统中,为了保证数据的一致性 和完整性,对多个节点上的数据进行操作的事务。当一个事务涉及到多个不同的数据库、服务或应用实例时,就构成了分布式事务。
比如上图,订单服务的数据操作和库存服务的数据操作保持一致(同时成功或同时失败),就是分布式事务的范畴。简单来说就是分布式数据一致性问题,也就是如何在分布式场景中保证多个节点数据的一致性。
举个例子,现在某一商品库存是充足的,但是某个用户的账户余额是不够的,此时客户端进行下单,对两个服务发送请求,库存验证发现充足,完成扣除库存;余额验证发现不充足,无法完成扣款,要提示下单失败。如果没有分布式事务,无法完成两个服务的同时回退,就会发生库存扣了,但是下单失败了,导致库存凭空消失。
2.X/Open 分布式事务模型
X/Open 是一个组织,X/Open DTP ( Distributed Transaction Process Reference Model) 是X/Open这个组织定义的一套分布式事务的标准。
这个标准提出了使用两阶段提交(2PC,Two-Phase-Commit) 来保证分布式事务的完整性。
这套标准主要定义了实现分布式事务的规范和API,具体的实现则交给相应的厂商来实现。
相关文档:DTP 参考模型
X/Open DTP参考模型包含三种角色:
|----|-----------------------------------------------------------|
| AP | Application,应用程序 |
| RM | Resource Manager,资源管理器,比如数据库。应用程序可以通过资源管理器对相应的资源进行有效的控制 |
| TM | Transaction Manager,事务管理器,一般指事务协调者,负责协调和管理各个子事务,可以理解为管理RM |

X/Open DTP模型的执行流程:
|------------------------------------------------------|
| 1. 配置TM,把多个RM注册到TM |
| 2. AP从TM管理的RM中获取连接,比如JDBC连接 |
| 3. AP向TM发起一个全局事务,生成全局事务ID(XID),XID会通知各个RM |
| 4. AP通过第二步获得的连接直接操作RM完成数据操作。AP在每次操作时会把XID传递给RM |
| 5. AP结束全局事务,TM会通知各个RM全局事务结束。根据各个RM的事务执行结果,执行提交或者回滚操作 |

3.两阶段提交
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消息,表示事务已成功回滚。

两阶段提交把一个事务的处理过程分为准备和提交/回滚两个阶段,采用简单的方式来解决分布式事务的问题,但是这个过程中,存在以下缺点:
|-------|---------------------------------------------------------------------------------------------------------------------------------|
| 阻塞问题 | 两个阶段都是事务阻塞型的,对于每一个指令都需要有明确的响应,如果在这个过程中,TM宕机或者网络出现故障,则会一直处于阻塞状态。比如第一阶段完成后TM宕机或网络出现故障了,此时RM会一直阻塞,无法进行其他操作。所以3PC针对此问题,加入了timeout机制 |
| 资源占用 | 参与者在收到准备请求后,会锁定相关资源以保证事务的原子性。在整个两阶段提交过程中, 这些资源一直被锁定,直到事务提交或回滚完成。这会导致资源利用率降低,其他事务可能因无法获取所需资源而等待 |
| 数据不一致 | 第二阶段中,TM向所有的RM发送commit请求,由于局部网络异常,导致只有一部分RM收到了commit请求,这些RM节点执行commit操作,没有收到commit请求的节点由于事务无法提交,出现数据不一致的情况 |
相应也存在以下优点:
|----------|-----------------------------------------------|
| 保证事务的原子性 | 2PC通过两个阶段的严格控制,确保了事务要么全部提交,要么全部回滚,从而保证了事务的原子性 |
| 实现相对简单 | 相对于其他分布式事务协议,2PC的实现相对简单,易于理解和实现 |
4.三阶段提交
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 消息后,如果网络出现故障,协调者和参与者无法进行后续通信,参与者在超时后可能会自行提交事务,导致数据不一致 |
5.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逻辑 |
缺点:
|----------------------------------------------|
| 需要开发人员手动编写三个阶段的业务逻辑,并保证其正确性和一致性,增加了开发难度和维护成本 |
| 需要考虑各种异常情况和边界情况,并提供相应的补偿策略和重试机制,增加了系统复杂度和风险 |
二.Seata
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
详细请看官网:Seata 是什么? | Apache Seata
1.术语
1)TC (Transaction Coordinator) - 事务协调者
维护全局和分支事务的状态,驱动全局事务提交或回滚
2)TM (Transaction Manager) - 事务管理器
定义全局事务的范围:开始全局事务、提交或回滚全局事务
3)RM (Resource Manager) - 资源管理器
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚
2.下载和部署
下载
下载地址:Seata Java Download | Apache Seata

修改配置
在修改配置前,先去**/seata-server/script/server/db/mysql.sql** 找到这个SQL文件,并执行里面的内容:
sql
CREATE DATABASE IF NOT EXISTS seata;
修改 /seata-server/conf/application.yml 中seata相关的配置:
seata:
config:
# support: nacos, consul, apollo, zk, etcd3
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace: ''
group: SEATA_GROUP
registry:
# support: nacos, eureka, redis, zk, consul, etcd3, sofa
type: nacos
nacos:
application: seata-server
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
store:
# support: file, db, redis, raft
mode: db
db:
datasource: druid
db-type: mysql
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true
user: root
password: root
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: 5000
启动
双击 /seata-server/bin/seata-server.bat
访问 http://127.0.0.1:7091/,用户名密码:seata/seata
3.集成
1)引入依赖
XML
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
2)修改配置文件
XML
seata:
registry: #定义了Seata Server的注册中⼼配置, 微服务根据配置信息去注册中⼼获取tc服务地址
type: nacos #指定注册中心的类型
nacos:
application: seata-server #Seata Server在Nacos中的应⽤名称
server-addr: 127.0.0.1:8848 #Nacos服务器地址
group: "SEATA_GROUP" #Seata Server在Nacos中的分组名称
namespace: "" #Nacos的命名空间, 设置为空, 表⽰使⽤默认的命名空间public
tx-service-group: default_tx_group #定义事务服务组的名称
service:
vgroup-mapping:
default_tx_group: default
微服务启动时,会根据 default_tx_group 查找Seata Server. Seata Server在启动时会注册到Nacos的 SEATA_GROUP 分组中,应用名称为 seata-server ,并映射到 default 集群。这样,Seata Client可以通过Nacos发现并连接到Seata Server,进行事务协调和管理。
三.Seata各事务模式
1.XA 模式
XA 模式是从 1.2 版本支持的事务模式。XA 规范 是 X/Open 组织定义的分布式事务处理标准。 Seata XA模式是利用事务资源(数据库、消息服务等)对 XA 协议的支持,以 XA 协议的机制来管理分支事务的一种事务模式。
XA实现的原理是基于两阶段提交。
优点:
|------------|-----------------------------------------------|
| 事务强一致性 | XA模式能够满足ACID原则,确保分布式事务的强一致性 |
| 实现简单且无代码侵入 | 常用数据库都支持XA协议,使用Seata的XA模式无需修改业务代码,只需进行简单的配置即可 |
缺点:
|----------|-----------------------------------------------------|
| 性能较差 | 一阶段需要锁定数据库资源,等待二阶段结束才释放,导致事务资源长时间得不到释放,锁定周期长,从而影响性能 |
| 依赖关系型数据库 | XA模式依赖数据库实现事务,对于一些非关系型数据库或不支持XA协议的数据库,无法使用 |
2.AT 模式
AT 模式是 Seata 创新的一种非侵入式的分布式事务解决方案。Seata 在内部做了对数据库操作的代理层,我们适用 Seata AT 模式时,实际上用的是 Seata 自带的数据源代理 DataSourceProxy,Seata 在这层代理中加入了很多逻辑,比如插入回滚 undo_log 日志,检查全局锁等。
3.TCC 模式
TCC 模式是 Seata 支持的一种由业务方细粒度控制的侵入式分布式事务解决方案,是继 AT 模式后第二种支持的事务模式,最早由蚂蚁金服贡献。其分布式事务模型直接作用于服务层,不依赖底层数据库,可以灵活选择业务资源的锁定粒度,减少资源锁持有时间,可扩展性好,可以说是为独立部署的 SOA 服务而设计的。
4.Saga 模式
Saga 模式是 SEATA 提供的长事务解决方案,在 Saga 模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。
5.四种模式对比
|-------|-----------------------------------------------------------------|---------------------------------------------------------------------|-------------------------------|--------------------------------------|
| | XA | AT | TCC | SAGA |
| 实现方式 | 依赖数据库对XA协议的支持,通过XA规范来实现事务的提交和回滚; 不需要额外的 undo_log 表,但要求数据库支持XA协议 | 通过记录数据快照 ( undo_log 表 ) 来实现数据的回滚; 适用于几乎所有的数据库,但需要在业务库中创建 undo_log 表 | 开发人员手动实现Try、Confirm、Cancel三阶段 | 事件驱动,每个事务包含正向操作和逆向补偿操作; 失败时按顺序执行逆向补偿 |
| 一致性 | 强一致性 事务的中间状态对用户不可见 | 最终一致性 在事务的两阶段之间,数据可能处于中间状态 | 最终一致性(通过业务实现) | 最终一致性 |
| 性能 | 性能较差,一阶段锁定资源,等待二阶段结束才释放 | 性能较好,一阶段直接提交,不锁定资源 | 较高,但开发成本高(需要处理空回滚,业务悬挂等) | 高(无锁) 适合长事务 |
| 代码侵入 | 无代码侵入 | 无代码侵入 | 有,需要手动编写三个接口 | 有,需要编写状态机和补偿业务 |
| 数据库支持 | 依赖数据库对XA协议的支持 | 适用于几乎所有支持SQL的数据库 | 不依赖底层数据库的事务机制 | 不依赖底层数据库的事务机制 |