Seata
Seata 场景有哪些角色呢? 它们的作用?⭐️⭐️
TC - Transaction Coordinator (事务协调器): 它是独立的中间件服务端(Seata-Server)。负责维护全局事务的运行状态,接收来自 TM 的请求来开始、提交或回滚全局事务,并与 RM 通信来驱动分支事务的提交或回滚。
TM - Transaction Manager (事务管理器): 嵌入在业务代码中(通常就是那个加了 @GlobalTransactional 注解的微服务)。它负责定义全局事务的边界。并且负责与 TC 配合来完成全局事务的管理
RM - Resource Manager (资源管理器): 控制分支事务的资源(通常是数据库)。它负责向 TC 注册分支事务、汇报分支事务的状态,并接收 TC 的指令来驱动本地事务的提交或回滚。
- TM 向 TC 申请开启一个全局事务,TC 返回一个全局唯一的 XID。
- RM 带着这个 XID 执行本地业务,并向 TC 汇报:“我这部分准备好了”。
- TM 根据业务结果,请求 TC 对这个 XID 进行全局提交或回滚。
- TC 调度 XID 下所有的 RM,指令它们同步完成提交或回滚。
讲一下 Seata AT 模式的工作过程(两个阶段)⭐️
-
一阶段(准备阶段):
- Seata 会拦截业务 SQL,在更新前先查一下数据长什么样(Before Image),更新后再查一下长什么样(After Image)。
- 把这两份快照存到数据库的
undo_log表里,然后直接无需像 XA 模式必须要等“总指挥”(事务管理器)发送“可以提交”的指令,而是直接提交本地事务, 释放数据库锁。
-
二阶段(提交/回滚):
- 如果成功:异步删除
undo_log记录就行了,性能很高。 - 如果失败:根据
undo_log里的 Before Image 把数据还原回去。
- 如果成功:异步删除
AT 模式是怎么解决“脏写”的?(全局锁机制)⭐️
既然一阶段本地事务就提交了,别的线程进来改了数据怎么办?
- 核心武器:全局锁 (Global Lock)。
- 流程:在一阶段本地提交前,分支事务必须向 TC(事务协调器)申请这行数据的“全局锁”。
- 逻辑:如果申请不到,说明别的事务正在改这行数据,本地事务必须重试或等待。
- 重点:这样就保证了在分布式环境下,同一时间只有一个事务能修改这行数据,避免了脏写。
在 AT 模式下,虽然一阶段本地事务提交后释放了数据库层面的行锁(DB Lock),但 TC(事务协调器)侧的全局锁(Global Lock)并未释放。其他受 Seata 管理的线程如果尝试修改同一行数据,会因为拿不到全局锁而进入重试或等待。只有等到二阶段完成(无论是全局提交还是全局回滚),全局锁才会被释放,从而在不长时间阻塞数据库连接的前提下,有效避免了脏写。”
如果业务执行时,数据库断电了或者 undo_log 丢了怎么办?⭐️
Seata 在回滚前会做一个校验。它会对比当前数据库里的数据和 undo_log 里的 After Image。如果这两个不一致,说明数据在事务期间被“跳过 Seata”手动改过了(发生了脏写),此时 Seata 会停止回滚,发送告警,需要人工介入。
为什么选 AT 模式而不选 TCC?
“AT 模式对业务无侵入,我只需要在方法上加个 @GlobalTransactional 注解就行了,不需要像 TCC 那样去手写 Try/Confirm/Cancel 三个接口,开发效率极高。对于我们大部分对性能要求不是极端苛刻的业务场景,AT 模式是首选。”
XA 模式你了解吗?⭐️
了解,它是分布式事务的“老大哥”。如果说 Seata AT 模式是“先斩后奏”,那么 XA 模式就是典型的“事事请示”。
在 Seata 中使用 XA 模式,其核心动作是完全依赖数据库的。
- 第一阶段(Prepare):
-
动作:事务管理器(TC)询问所有参与者(RM):“大家都准备好了吗?”
- 执行:各数据库执行本地 SQL,但不提交事务。此时,数据库会锁定资源(行锁),等待最后的指令。
-
第二阶段(Commit/Rollback):
- 动作:如果大家都说 OK,TC 发送 Commit 指令;只要有一个不行,TC 发送 Rollback。
- 执行:数据库真正提交或回滚,释放锁。
“XA 模式是基于数据库原生协议的强一致性方案。它的优点是完全交给数据库处理,非常安全;但缺点是锁资源的时间太长,从第一阶段开始直到第二阶段结束,数据库的行锁都不释放。
相比之下,我项目里选用的 AT 模式 通过 undo_log 把‘物理锁’变成了‘逻辑锁’,一阶段就释放了数据库连接,在高并发场景下性能表现更好。”