分布式事务Seata AT模式设计分析篇
分布式事务Seata AT模式设计分析篇
1、介绍
官网: https://seata.apache.org/zh-cn/docs/user/mode/at
Seata AT模式是分布式事务的轻量级解决方案。适合快速接入、业务无侵入,允许短暂数据不一致使用。
Seata AT模式,属于自动化2PC机制,在第一阶段直接提交业务数据和Undo Log(本地事务提交),在本地事务提交前通过TC(Transaction Coordinator 事务协调者)注册全局锁接管保证隔离性,后期通过日志(根据undo log自动生成反向SQL日志)实现最终一致性。
2、原理分析
1、全局锁设计
全局锁极大提高了吞吐量,如下分析
传统2PC的行锁阻塞(从 Prepare 到 Commit/Rollback 期间,数据库行锁持续占用,IO级别)时间长(秒级500ms~2s)导致的锁持有时间长吞吐量骤降(如秒杀场景),通过Seata AT模式的极短时间的全局锁提高了吞吐量可支持互联网高并发要求,原因是在一阶段本地事务是直接提交,在提交事务前通过TC 内存中的轻量全局锁(逻辑锁:为内存锁)接管了资源,而内存级全局锁管理效率远高于数据库锁,二阶段开始时就通知TC删除了全局锁,全局锁占用的时间非常短(1~10ms),也没有数据库锁的IO阻塞问题,内存级的冲突检测效率提升100倍+,并且全局事务提交由 TC 异步批量处理(不阻塞业务线程)。
全局锁的生命周期为在本地事务提交前向 TC 注册分支事务并申请全局锁,若申请失败,本地事务直接回滚(不提交),申请成功则在本地事务提交成功的瞬间(COMMIT
完成)即生效全局锁。在二阶段开始时立即释放(非二阶段结束后)TC删除全局锁。
全局锁降低锁冲突提高吞吐量主要原因可总结如下:
- 全局锁持有的时间极短:传统2PC数据库锁需要在Prepare 阶段 → Commit 结束一直持有,时间需要(秒级500ms2s),而Seata全局锁持有是在一阶段结束开始到二阶段开始前,只需毫秒级别**(110ms)**。
- 锁冲突检测效率高并且冲突时立即拒绝: 传统2PC(数据库)需要磁盘I/O,行锁检测耗时5-20ms,资源消耗高,而Seata全局锁为内存级别,使用了内存哈希表,检测耗时只需0.01ms,几乎没有资源消耗。全局锁的轻量化由于TC 使用
ConcurrentHashMap
管理锁,如下,锁判断效率为O(1) 时间复杂度。
// Seata 核心源码简化
public class DefaultLockManager {private final ConcurrentMap<String, Set<String>> lockStore = new ConcurrentHashMap<>();// key: 资源ID(如stock.1), value: 持有锁的事务XID集合
}
在发生冲突时,传统2PC阻塞等待属于悲观锁情况,导致线程堆积甚至导致系统雪崩。Seata的全局锁冲突时立即拒绝,快速失败,资源立即释放系统雪崩概率低,虽然也属于悲观锁,但是由于锁持有时间极短,实际效果趋近乐观锁。
- 锁范围精准控制: 假设更新id=1,即使id为唯一索引时传统2PC锁范围只锁定id=1这行,如果稍微复杂点的sql锁升级可能就会有间隙锁(Gap Lock)污染,导致数据库无法在范围内插入数据。而Seata代理层解析 SQL 的精确操作对象,无论sql多复杂锁定目标资源id,规避了数据库间隙锁等问题。在内存中按资源 ID 独立存储锁。
示例:假设并发请求间隔为 50ms:
- 传统 2PC:锁占用 500ms → 可能阻塞 10 个请求(500/50)
- Seata AT:全局锁占用 5ms → 仅可能阻塞 0.1 个请求(5/50)
冲突概率降低 100 倍
2、回滚日志设计
Seata AT 回滚日志设计使业务可正常回滚及保证数据一致性。
在一阶段中,Seata会拦截“业务SQL”,首先解析SQL语义,找到要更新的业务数据,在数据更新前保存数据快照日志(before image 即 undo log”),然后执行“业务SQL”更新数据,更新之后保存数据快照(“after image”SQL 执行后的数据快照,非传统 redo log),本地事务提交前生成在TC注册全局锁,防止其他事务修改相同数据。这些操作都是在本地数据库事务内完成,这样保证了一阶段的原子性。分阶段如下:
- SQL拦截:Seata代理数据源,拦截业务SQL。
- 解析语义:分析SQL类型(UPDATE/INSERT/DELETE)
- 记录前镜像:查询并保存数据更新前的状态(before image → undo log),(用于回滚时生成反向 SQL)
- 执行业务SQL:执行实际的数据修改操作
- 记录后镜像:业务 SQL 执行后查询并保存数据更新后的状态(after image非传统 redo log),(用于回滚时校验数据一致性)
- 生成行锁:在TC注册全局锁,防止其他事务修改相同数据
- 提交本地事务:将业务数据+undo log+after image原子性提交
相对一阶段,二阶段比较简单,负责整体的回滚或提交。如果之前的一阶段中有本地事务没有通过,那么执行全局回滚,否则执行全局提交。回滚用到的就是一阶段记录的“undo log”,通过undo log生成反向更新SQL并执行,以完成分支事务的回滚。当然,事务完成后会释放所有资源和删除所有日志。
提交是异步化的,由TC 异步批量处理(不阻塞业务线程),仅删除Undo Log,无需数据操作(秒级完成)
同步阻塞回滚补偿:根据Undo Log生成反向SQL回滚数据,保证数据一致性
3、集群模式下强一致性设计
虽然全局锁是内存级别的,在集群环境下依然能保证强一致性,并非只有一台机器能锁住资源。其核心是通过 TC(事务协调器)集群 + Raft 共识协议实现分布式锁的高可用和一致性。
- 锁数据位置:TC Leader 内存中维护锁映射表
- 数据持久化:通过 Raft 日志同步到所有 Follower
- 锁键结构:
资源ID -> 持有者XID
(如stock:1001 -> xid:9527
)
锁申请流程(集群版)
锁操作需多数节点(N/2+1)确认后才生效,集群牺牲部分性能,换取 高可用 + 强一致
3、应用场景分析
简单跨服务事务,低冲突更新。适合快速接入、业务无侵入,允许短暂的数据不一致,是简单分布式事务的首选。金融级强一致性或超高频热点更新场景,应评估TCC/Saga模式替代方案。不适合如下场景
场景 | 原因 |
---|---|
金融核心转账(强一致性) | AT默认读未提交隔离,可能脏读 |
秒杀热点库存更新 | 全局锁竞争导致高冲突率(失败率>30%) |
长事务流程(>1分钟) | Undo Log长期占用数据库资源 |
嵌套分布式事务 | AT不支持嵌套事务 |