[toc]
| 单词 | 词性 | 音标 | 含义 |
|---|---|---|---|
| transaction | n. | /trænˈzækʃn/ | 事务(一组要么全部成功、要么全部失败的操作) |
| distributed | adj. | /dɪˈstrɪbjuːtɪd/ | 分布式的(跨多个服务/数据库,不再局限于单库单表) |
| consistency | n. | /kənˈsɪstənsi/ | 一致性(所有参与方数据是否都保持一致) |
| compensate | v. | /ˈkɒmpenseɪt/ | 补偿(回滚或反向操作,把已提交的部分恢复一致) |
| global | adj. | /ˈɡləʊbl/ | 全局的(这里的"全局事务"指跨多个本地事务的整体事务) |
Lecture10-SpringCloud-Seata分布式事务
目标:定性理解为什么微服务会面临"分布式事务"问题,掌握 CAP/BASE 基本思想,了解 Seata 的 AT/TCC/XA/SAGA 四种模式与适用场景。本节不搭建、不实操、不演示。
一、理论概念
1、单库事务 vs 分布式事务
单库事务(本班已学):一个数据库内,@Transactional 保证 ACID(原子/一致/隔离/持久)。底层执行时,能够保证使用同一个数据库连接:Connection 对象
下单 = 扣库存 + 写订单表 (同一个数据库,一个事务搞定)
分布式事务:服务拆了、数据库也拆了,"扣库存"在库存库、"写订单"在订单库,不在同一个事务里了:
order-service 扣库存(库存库)┐ 各自独立事务,如何保证一起成功/一起失败?
product-service 写订单(订单库)┘
如果扣库存成功、写订单失败,就会出现"钱扣了、单没下成"的不一致。这就是分布式事务要解决的问题。
由于它们分属于不同微服务、不同数据库,所以无法纳入同一个传统数据库事务来完成;
把多个操作纳入一个逻辑上的整体这个需求仍然是刚性存在的,所以呼唤新的技术手段!
2、两个基础理论:CAP 与 BASE
1、CAP 定理
分布式系统在**强一致性(C)、可用性(A)、分区容忍性(P)**三选二,P(分区容忍性)必须保留,实际是在 C 和 A 之间取舍。
- 强一致性(C):通过加锁保证数据的计算哪怕是在高并发情况下仍然正确
- 可用性(A):系统对外有较好的性能表现,强调并发请求高效处理,天然排斥加锁
- 分区容错性(P):整个项目即使局部故障,也仍然保证整个项目整体上是可用的
| 组合 | 含义 | 代表 |
|---|---|---|
| CP | 强一致,网络分区时牺牲部分可用 | ZooKeeper、etcd |
| AP | 高可用,分区时允许暂时不一致 | Eureka、多数业务系统 |
| 单点 CA | 不分区才能同时满足 | 单机数据库 |
微服务注册中心(Nacos 等)多是 AP(先保证可用,节点间最终一致)。业务系统则普遍采用"最终一致"而非强一致。
2、BASE 思想
BASE = Basically Available(基本可用)+ Soft state(软状态)+ Eventually consistent(最终一致)。
核心:不强求"任何时刻都一致",而是保证最终一致——中间允许短暂不一致,通过补偿/重试等机制最终对齐。
一句话:CAP 告诉你"必须取舍",BASE 告诉你"选最终一致也能用"。 Seata 就是实现"最终一致"的工具。
3、分布式事务的经典解法:2PC / 3PC / XA / TCC
在讲 Seata 之前,先认识几个"前辈"——分布式事务领域几十年沉淀下来的经典协议。Seata 的四种模式,本质上都是这些思路的工程化版本。
1、两阶段提交(2PC, Two-Phase Commit)
最经典的分布式事务协议,把"提交"拆成两步:
阶段一:准备(Prepare)
协调者问所有参与者:"你能提交吗?"
各参与者执行本地事务,但先不真正提交,锁住资源,回复 Yes / No
阶段二:提交或回滚
全部 Yes → 协调者通知所有参与者正式提交
任一 No → 协调者通知所有参与者回滚
协调者 ──Prepare──> 参与者A(执行+锁资源)──Yes──>
──Prepare──> 参与者B(执行+锁资源)──Yes──>
──Commit──> 参与者A、B(正式提交)
三个天生的问题:
| 问题 | 说明 |
|---|---|
| 同步阻塞 | 阶段一到阶段二之间,资源被锁住,其他请求只能等,整体执行效率低 |
| 协调者单点故障 | 协调者挂了,参与者一直持有锁、阻塞等待 |
| 数据不一致 | 阶段二网络分区,部分参与者收到 commit、部分没收到 |
2、三阶段提交(3PC, Three-Phase Commit)
针对 2PC 的阻塞问题做改进:
- 把"准备"拆成 CanCommit → PreCommit → DoCommit 三步
- 参与者加了超时机制:等不到协调者指令,超时后自动提交(减少永久阻塞)
CanCommit(问能不能)→ PreCommit(锁资源、记日志)→ DoCommit(正式提交)
3PC 改善了阻塞,但网络分区时仍可能不一致,工程上很少真用——它更多是个理论演进的中间产物。
3、XA 协议
XA 是 X/Open 组织定义的数据库层面的分布式事务工业标准。
- 关系:2PC 是"思想",XA 是这个思想的标准实现规范
- MySQL、Oracle、PostgreSQL 都原生支持 XA
- Java 里的 JTA(Java Transaction API)就是对接 XA 的接口
Java 应用 ──JTA──> XA 协议 ──> MySQL 1
──> MySQL 2
Seata 的 XA 模式,本质就是让 Seata 直接调用数据库原生的 XA 接口,不自己造轮子。
4、TCC(Try-Confirm-Cancel)
把两阶段从"数据库层"上移到"业务层",由业务自己写三个方法:
| 阶段 | 做什么 | 例子(转账) |
|---|---|---|
| Try | 尝试执行,预留资源 | 把 A 账户的 100 元"冻结"(不真扣) |
| Confirm | 确认执行,真正提交 | 冻结的 100 元真正扣掉,加到 B 账户 |
| Cancel | 取消执行,释放预留 | 解冻,把 100 元退回 A 账户 |
和 2PC 的关键区别:
| 2PC / XA | TCC | |
|---|---|---|
| 锁在哪 | 数据库行锁(真锁) | 业务层"冻结"(逻辑上的锁) |
| 谁写代码 | 数据库自己处理 | 业务方手写 Try/Confirm/Cancel 三个方法 |
| 侵入性 | 低 | 高 |
| 性能 | 锁持有期间阻塞 | 不锁数据库行,并发好 |
5、四者关系一图流
| 层次 | 锁资源 | 阻塞 | 侵入性 | 实际用在哪 | |
|---|---|---|---|---|---|
| 2PC | 协议思想 | 数据库行锁 | 严重 | 低 | 理论基础 |
| 3PC | 2PC 改进 | 数据库行锁 | 改善 | 低 | 几乎不用 |
| XA | 数据库标准 | 数据库行锁 | 严重 | 低 | Seata XA 模式 |
| TCC | 业务层 | 业务冻结 | 不锁 | 高 | Seata TCC 模式 |
过渡到 Seata:Seata 的 AT 模式其实是 2PC 的"改良版"——它用 UNDO_LOG 代替真锁,阶段一就提交本地事务(不阻塞别人),失败时再按日志反向“回滚“。这就是为什么 AT 性能比 XA 好、又比 TCC 侵入性低。
二、具体产品
1、Seata 是什么
Seata(Simple Extensible Autonomous Transaction Architecture)是阿里巴巴开源的分布式事务框架。核心角色:
┌──────────────┐
│ TC 事务协调器 │ 负责全局事务的提交/回滚决策
└──────┬───────┘
┌──────────┼──────────┐
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│ TM 事务 │ │ RM 资源 │ │ RM 资源 │
│ 管理器 │ │ 管理器 │ │ 管理器 │
│(发起全局│ │(负责某库│ │(负责某库│
│ 事务) │ │ 的分支) │ │ 的分支) │
└────────┘ └────────┘ └────────┘
- TM(事务管理器):发起全局事务、决定提交/回滚。
- TC(事务协调器):全局协调,维护全局事务状态。
- RM(资源管理器):管理各分支(各服务自己的本地事务)。
TC 和 TM 怎么区分?
最容易混的两个角色,一句话先记住:TM 是"做决定的人",TC 是"记账本 + 传令兵"。
类比银行转账:
| 角色 | 类比 | 在哪 |
|---|---|---|
| TM | 你(在 App 上点"转账") | 你的微服务代码里(@GlobalTransactional 注解的方法) |
| TC | 银行总行的中央记账系统 | 独立部署的 Seata Server |
| RM | 各分行的柜台 | 每个微服务自己 |
一次完整流程:
你点"转账 100 元"
├─ TM:"TC,帮我开一个全局事务,ID=X123"
│ TC:记下了,X123 开始
├─ TM 调 A 扣款 → RM-A 执行 → TC:记下分支 A
├─ TM 调 B 加钱 → RM-B 执行 → TC:记下分支 B
└─ TM:"TC,X123 提交!"
TC:查账本——A、B 都 OK,通知 A 提交、B 提交
关键区别:
| TM | TC | |
|---|---|---|
| 干什么 | 发起全局事务,决定提交还是回滚 | 记录全局事务状态,通知各分支执行 |
| 在哪跑 | 你的微服务里 | 独立部署的 Seata Server |
| 有几个 | 很多个 | 一个(或一个集群) |
| 谁写代码 | 你写(业务代码) | 不写(用现成的 Seata Server) |
为什么 TM 不自己通知 RM,非要 TC 居中?
因为 TM 做完决定就可能挂了。比如:TM 告诉 RM-A"扣成功",正准备告诉 RM-B 时进程崩了——RM-A 扣了、RM-B 没加上,数据就不一致了。
TC 的存在就是为了解决这个问题:TC 把"决策结果"持久化在自己这里,TM 挂了也没关系,TC 重启后接着把剩下的 RM 通知完。这就是为什么 TC 必须独立部署、必须有持久化。
| 常见误区 | 正解 |
|---|---|
| TM = 协调者 | 错,TC 才是协调者。TM 只是"发起者 + 决策者" |
| TM 负责回滚 | 错,TM 只做决定"要不要回滚",真正通知各 RM 回滚的是 TC |
| TC 是某个微服务 | 错,TC 是独立的 Seata Server,不跑业务代码 |
2、四种模式对比(概念)
| 模式 | 思路 | 侵入性 | 适用 |
|---|---|---|---|
| AT | 业务无感,框架自动生成反向回滚(UNDO_LOG) | 低 | 最常用,适合多数业务 |
| TCC | 业务方手写 Try / Confirm / Cancel 三个方法 | 高 | 高一致性、性能敏感场景 |
| XA | 数据库原生 XA 协议,强一致 | 中 | 强一致要求、性能可接受场景 |
| SAGA | 长事务拆成子事务,失败逐级逆向补偿 | 中 | 长流程、跨长时间(如聚合支付) |
3、AT 模式详解(重点)
AT 模式是 Seata 最常用方案,业务代码几乎不用改("无侵入"):
阶段一(本地执行 + 记录回滚日志):
各服务各自提交本地事务,同时把"修改前数据"记进 UNDO_LOG
此时数据已变,但先不告诉别人最终结果(软状态)
阶段二(协调器统一决策):
全部成功 → 各分支宣告操作完成,删除 UNDO_LOG
任一失败 → 各分支按 UNDO_LOG 执行逆操作,把数据修改成事务执行之前的样子(补偿)
业务里只需在最外层方法加一个注解(伪代码示意):
// 伪代码:最外层开启全局事务
@GlobalTransactional
public void createOrder(Long userId, Long productId) {
inventoryService.deduct(productId); // 扣库存(分支一)
orderService.save(order); // 写订单(分支二)
// 任一步失败,Seata 自动回滚另一个已提交的分支
}
理解要点:业务只管"正常写",Seata 靠 UNDO_LOG 在失败时自动反向回滚——这是 AT 模式"侵入性低"的原因。
三、总结
1、什么时候用分布式事务
| 场景 | 建议 |
|---|---|
| 跨库/跨服务强一致(钱、库存、订单) | 用 Seata(AT 为主) |
| 能容忍最终一致、异步化 | 优先用"本地消息表 + 消息队列"实现最终一致(更轻量) |
| 可重试的写操作 | 先考虑"重试 + 幂等"能不能解决,能就不必上分布式事务 |
原则:分布式事务是"最后手段"。能靠最终一致/异步/幂等解决,就不要引入它的复杂度和性能开销。
2、课堂练习(概念问答)
- 用一句话说明"为什么拆了服务就会遇到分布式事务问题"。
- CAP 里为什么 P 必须保留?业务系统通常选 C 还是 A?
- 2PC 有哪三个天生问题?TCC 用什么办法绕过"数据库行锁",代价是什么?
- AT 模式为什么业务代码几乎不用改?(提示:UNDO_LOG 自动回滚)
- 思考:你的项目是"扣库存 + 下单",你会选 Seata 还是"本地消息 + 消息队列"?为什么?
3、本讲小结
- 服务/库拆分后,"跨库跨服务保持一致"就是分布式事务问题。
- 理论:CAP 要取舍,BASE 走最终一致。
- 经典解法演进:2PC(两阶段锁资源)→ 3PC(加超时改善阻塞)→ XA(数据库标准)→ TCC(业务层 Try/Confirm/Cancel)。Seata 四模式是这些思路的工程化。
- Seata 四模式:AT(无侵入、最常用)/ TCC(高一致)/ XA(强一致)/ SAGA(长流程)。
- AT 两阶段:先本地执行 + 记 UNDO_LOG,再由协调器统一提交或反向回滚。
- 原则:能异步最终一致就尽量别上分布式事务。
4、本节验证
- 理解单库事务 vs 分布式事务的区别 ✅
- 掌握 CAP 定理与 BASE 思想 ✅
- 了解 2PC / 3PC / XA / TCC 的基本思路与相互关系 ✅
- 理解 Seata 四种模式(AT / TCC / XA / SAGA)的思路与适用场景 ✅
- 会判断什么时候需要分布式事务 ✅