
分布式事务解决方案
AI摘要
北海のAI
一个下订单的业务中扣减库存、创建订单、扣减余额等在分布式情况下由于位于不同的分支事务,存在OpenFeign这种远程调用,以及@Transactional注解这种不可以跨服务的去回滚事务,所以如何解决这种分布式事务在企业中是常见的问题,下面是我的思路与总结,如果你有更好的方案或我这里有错误请在评论区去指出
Seata解决
Seta是阿里巴巴提供的这种解决分布式事务一站式的方案,其中默认模式是AT,其次是XA、TCC、Saga,现在依次对这些不同的解决方式去介绍
flowchart TB
subgraph Legend["📋 图例说明"]
direction LR
L1["🔵 TC: 事务协调器(Transaction Coordinator)"]:::tc
L2["🟢 TM: 事务管理器(Transaction Manager)"]:::tm
L3["🟡 RM: 资源管理器(Resource Manager)"]:::rm
L4["🔴 存储层:持久化数据"]:::storage
end
subgraph Core["Seata 核心架构组成"]
direction TB
subgraph TC["🔵 TC Server(事务协调器)
维护全局事务和分支事务状态,驱动全局提交或回滚"]
direction TB
TC1["事务协调器集群
TC Cluster"] --> TC2["全局事务管理
Global Transaction Manager"]
TC2 --> TC3["分支事务注册中心
Branch Registry"]
TC3 --> TC4["全局锁管理器
Global Lock Manager"]
TC4 --> TC5["事务状态机
Transaction State Machine"]
TC5 --> TC6["二阶段协调器
2-Phase Coordinator"]
TC6 --> TC7["提交处理器
Commit Processor"]
TC6 --> TC8["回滚处理器
Rollback Processor"]
TC7 & TC8 --> TC9["异步任务执行器
Async Worker"]
end
subgraph Storage["🔴 存储层(TC 持久化)"]
direction TB
S1[("事务会话存储
Session Store")] --> S2["全局事务表
global_table"]
S1 --> S3["分支事务表
branch_table"]
S1 --> S4["全局锁表
lock_table"]
S1 --> S5["分布式 KV 存储
DB / Redis / Raft"]
end
subgraph TM["🟢 TM(事务管理器)
定义全局事务范围,开启/提交/回滚全局事务"]
direction TB
TM1["@GlobalTransactional
注解拦截器"] --> TM2["全局事务上下文
Global Transaction Context"]
TM2 --> TM3["XID 传播器
XID Propagator"]
TM3 --> TM4["事务拦截器
Transaction Interceptor"]
TM4 --> TM5["事务模板
Transactional Template"]
TM5 --> TM6["开启全局事务
Begin Global TX"]
TM5 --> TM7["提交全局事务
Commit Global TX"]
TM5 --> TM8["回滚全局事务
Rollback Global TX"]
end
subgraph RM["🟡 RM(资源管理器)
管理分支事务处理资源,驱动分支事务提交或回滚"]
direction TB
RM1["资源管理器注册
RM Registry"] --> RM2["分支事务处理器
Branch Handler"]
RM2 --> RM3["AT 模式处理器
UNDO_LOG Manager"]
RM2 --> RM4["TCC 模式处理器
Try/Confirm/Cancel"]
RM2 --> RM5["Saga 模式处理器
State Machine Engine"]
RM2 --> RM6["XA 模式处理器
XA Resource Manager"]
RM3 --> RM7["数据源代理
DataSource Proxy"]
RM4 --> RM8["TCC 代理
TCC Proxy"]
RM5 --> RM9["Saga 执行器
Saga Executor"]
RM6 --> RM10["XA 连接池
XA Connection Pool"]
RM7 --> RM11["SQL 解析器
SQL Parser"]
RM7 --> RM12["UNDO_LOG 生成器
UNDO_LOG Generator"]
RM7 --> RM13["本地事务执行器
Local TX Executor"]
end
subgraph Client["🟣 Seata Client(集成在业务应用)"]
direction TB
C1["业务应用程序
Business Application"] --> C2["Seata Client SDK"]
C2 --> C3["配置中心客户端
Config Client"]
C2 --> C4["注册中心客户端
Registry Client"]
C2 --> C5["Netty 通信客户端
Netty Remoting Client"]
end
end
subgraph Interaction["组件交互关系"]
direction TB
I1["TM 开启全局事务"] -->|"1. 申请 XID"| I2["TC 分配 XID"]
I2 -->|"2. 返回 XID"| I3["TM 执行业务"]
I3 -->|"3. 调用服务"| I4["RM 注册分支"]
I4 -->|"4. 申请全局锁"| I5["TC 管理锁"]
I5 -->|"5. 批准/拒绝"| I6["RM 执行本地事务"]
I6 -->|"6. 上报状态"| I7["TC 更新状态"]
I7 -->|"7. 发起二阶段"| I8["TC 通知 RM"]
I8 -->|"8. 提交/回滚"| I9["RM 完成分支"]
end
%% 连接关系
TC -->|"持久化"| Storage
TM -.->|"注册全局事务"| TC
RM -.->|"注册分支事务"| TC
RM -.->|"申请/释放全局锁"| TC
Client -->|"包含"| TM
Client -->|"包含"| RM
%% 样式定义
classDef tc fill:#E3F2FD,stroke:#1976D2,stroke-width:3px,color:#000
classDef tm fill:#E8F5E9,stroke:#388E3C,stroke-width:2px,color:#000
classDef rm fill:#FFF8E1,stroke:#FBC02D,stroke-width:2px,color:#000
classDef storage fill:#FFEBEE,stroke:#C62828,stroke-width:2px,color:#000
classDef client fill:#F3E5F5,stroke:#7B1FA2,stroke-width:2px,color:#000
class TC,TC1,TC2,TC3,TC4,TC5,TC6,TC7,TC8,TC9 tc;
class TM,TM1,TM2,TM3,TM4,TM5,TM6,TM7,TM8 tm;
class RM,RM1,RM2,RM3,RM4,RM5,RM6,RM7,RM8,RM9,RM10,RM11,RM12,RM13 rm;
class Storage,S1,S2,S3,S4,S5 storage;
class Client,C1,C2,C3,C4,C5 client;
class Legend,L1,L2,L3,L4 default;
AT模式
AT模式是采用两阶段去提交即一阶段去提交 -> 二阶段靠undo_log去补偿,使用全局锁 + 本地锁 + undo_log,锁开销中等,适用于通用业务,并发适中
- 第一阶段:执行各自的本地分支事务,并且更新各自的undo_log数据表
- 第二阶段
- 成功:删除各自的undo_log数据表中数据
- 失败:对照各自的undo_log数据表中前后数据,进行回滚
1 | // 业务代码(开发者视角,无感知) |
XA模式
两阶段提交,一阶段不提交 -> 二阶段由数据库原生支持提交/回滚 ,这种方式XA 阻塞性更强,一致性更强,性能更低,金融核心用它稳,高并发下容易性能崩; 锁久阻塞是大坑,短事务低并发才可行。
- 一阶段:业务SQL去执行但是不提交,持有数据库的锁
- 二阶段:
- 成功:TC通知后,自动XA Commit
- 失败:自动XA Rollback
1 | # application.yml - 订单服务 |
TCC模式
三阶段提交:Try - Confirm - Cancel
- 一阶段Try(资源预留):执行业务检查,预留表要的资源,比如冻结库存、预扣金额,结果是资源被锁定,等待后续指令
- 二阶段Confirm(确认执行):真正执行业务,使用Try预留的资源,比如将冻结库存转换成实际扣减,预扣金额转成实际扣减。这里必须成功,失败就需要人工去接入
数据库表设计
1 | -- 库存表:增加 frozen 字段,记录"冻结"的库存 |
Java代码
1 | /** |
TM入口
1 |
|
本文是原创文章,采用CC BY-NC-SA 4.0协议,完整转载请注明来自北海博客-码海撷贝
评论 ()






