连载中 12/18

Seata AT 原理:undo_log 与全局锁

2026-06-20 · 6189 阅读 · 0 评论 · 0 赞

零侵入的魔法从哪来

AT 模式最迷惑人的地方:业务代码一行不改,回滚全自动——补偿逻辑从哪冒出来的?答案藏在代理数据源里:Seata 把你的 DataSource 包了一层,所有 SQL 先经过它。它做的事一句话概括:执行前拍张照(前镜像)、执行后拍张照(后镜像)、把两张照片存进 undo_log 表。回滚就是看照片逆操作。拆开一阶段二阶段细看。

一阶段:本地事务提前提交

拦截 SQL: update account set balance=90 where id=1
1 查前镜像:  select balance from account where id=1  → 100
2 执行 SQL:  balance 100 → 90
3 查后镜像:  select balance from account where id=1  → 90
4 组装 undo_log(前后镜像 + SQL 信息)写入本库
5 本地事务提交:业务 SQL + undo_log 原子落库
6 向 TC 注册分支并汇报状态

注意第 5 步——一阶段结束时,本地事务已经提交了,数据库行锁释放了。这是 AT 与 2PC 的根本区别:2PC 在 prepare 后锁着资源等全局指令;AT 把「等」的时间压缩到了极限,各分支的本地事务自顾自提交,全局的成与败交给二阶段处理。这就是 AT 吞吐高的根源。

二阶段:提交很快,回滚要验

全局提交:数据已经落库,什么都不用做,TC 通知各分支异步批量删除 undo_log 即可——提交路径近乎零成本。

全局回滚:RM 收到回滚通知后分两步走:先校验——把 undo_log 里的后镜像和当前表数据比对,一致说明这一行没人动过,安全;再回滚——根据前镜像自动生成反向 SQL(update 回原来的值、delete 掉 insert 的行)执行,最后删 undo_log。若比对不一致,说明有别人在全局事务外改了这行——脏写发生,反向 SQL 会覆盖别人的修改,Seata 拒绝执行并告警转人工。防脏写是 AT 的生命线。

全局锁:写在 TC 上的轻量锁

一阶段就提交了本地事务,两个全局事务并发改同一行怎么办?A 事务改了 balance 还没走到二阶段,B 事务也来改——A 回滚时发现数据被 B 改了,脏写。Seata 的答案是全局锁:分支事务在提交本地事务之前,必须先去 TC 拿到这行的全局锁(表名 + 主键维度);拿不到就自旋重试,超时则一阶段整体回滚。全局锁管住「全局事务之间」的写冲突,数据库行锁管住「本地并发」,两层各司其职。

锁从数据库行锁挪到 TC 内存,持有时长从「整个事务」缩短为「一阶段的瞬间」——这是 AT 快的另一半原因。但反过来看,锁压到 TC 也意味着热点行是 AT 的天敌:一万笔事务同时抢某账户这一行的全局锁,自旋重试会把吞吐打穿,这正是下一篇的主题。

隔离性的诚实交底

AT 的隔离级别要交底清楚:写隔离靠全局锁保证;读隔离默认没有——一阶段本地事务已提交,别的事务立刻能读到「全局上还没决定」的数据,相当于全局读未提交。Seata 提供加 @GlobalLock 或 select for update 的全局读方案,但都会拉低性能。所以业务设计要接受「中间态可见」,或者把强读一致的部分拆回本地事务——这与第 2 篇 BASE 的精神一脉相承。

维度AT 表现
业务侵入零(数据源代理 + 注解)
补偿方式前后镜像自动生成反向 SQL
一致性最终一致,默认读未提交
额外要求每个业务库建 undo_log 表,只支持关系库
软肋热点行全局锁竞争、脏写只能人工

小结

AT 一句话:代理数据源记前后镜像,一阶段本地事务提前提交换吞吐,二阶段按镜像反向回滚;全局锁压在 TC 防脏写,读未提交是默认,热点行是软肋。原理懂了,下一个问题接踵而至:账户余额这种天然的行热点,AT 抢锁抢到怀疑人生,TCC 冻结字段才是出路——下一篇:热点账户的锁冲突突围。

☕
503

10 年全栈工程师 · 503咖啡馆主理人

#分布式事务#Seata#AT模式#undo_log#全局锁

评论 (0)

热门推荐

连载中 11/22

主从搭建实操:从零配出一主两从

光讲原理不过瘾?手把手搭一主两从:my.cnf 六个参数、复制账号、GTID、CHANGE REPLICATION SOURCE TO、SHOW REPLICA STATUS 验收,附翻车排查清单。

#MySQL#主从复制#GTID#主从搭建#高可用
2026-05-07 · 10100 阅读 · 0 评论 · 0 赞
连载中 16/22

连接池:HikariCP 参数与连接风暴

连接池不是越大越好:8 核机器配 1000 连接反而更慢的数学原理,HikariCP 四个必调参数,maxLifetime 与 wait_timeout 的隐形陷阱。

#MySQL#连接池#HikariCP#maxLifetime#连接风暴
2026-05-10 · 9872 阅读 · 0 评论 · 0 赞
连载中 4/16

缓存穿透:恶意 ID 打穿 MySQL 的四道防线

请求的数据在缓存和数据库里都不存在时,缓存形同虚设。聊聊参数校验、空值缓存、布隆过滤器、限流熔断四道防线的原理与组合打法。

#Redis#缓存穿透#布隆过滤器#高可用
2026-05-16 · 9293 阅读 · 21 评论 · 287 赞