个人网站

前面讲了分布式事务的方案,Seata 是目前最主流的分布式事务框架,它的 AT 模式号称"业务零侵入"——怎么做到的?面试官问这题,他想听的是:你能不能讲清 AT 模式的一阶段/二阶段流程、undo_log 的原理、以及全局锁的作用?

先说结论

| 维度 | 说明 ||
| ------|------||
| 是啥 | 阿里开源的分布式事务框架 ||
| 核心组件 | TC(协调者)、TM(事务管理器)、RM(资源管理器) ||
| AT 模式 | 一阶段提交 + undo_log 回滚,业务零侵入 ||
| TCC 模式 | Try/Confirm/Cancel,需业务实现三接口 ||
| Saga 模式 | 正向+补偿链,长流程场景 ||
| 核心优势 | AT 模式无需改造业务代码 ||

|一句话记住:Seata AT模式像"带后悔药的提交"——先提交,后悔了用undo_log撤回"

三大核心角色

TC(Transaction Coordinator)事务协调者
├── 维护全局事务状态
├── 决定提交或回滚
└── 独立部署的 Seata Server

TM(Transaction Manager)事务管理器
├── 开启全局事务
├── 决定提交或回滚
└── 通常是发起方服务

RM(Resource Manager)资源管理器
├── 管理分支事务
├── 注册分支、上报状态
└── 每个参与服务都有一个
TM ──开启全局事务──→ TC ──返回XID──→ TM
TM(带XID) ──调服务A──→ RM_A ──注册分支──→ TC
TM(带XID) ──调服务B──→ RM_B ──注册分支──→ TC
TM ──提交/回滚──→ TC ──通知各RM──→ RM_A, RM_B

AT 模式:一阶段提交

AT 模式的核心是 一阶段就直接提交本地事务,不像 2PC 那样锁住资源等待。

一阶段流程

// 业务代码,完全不用改!
@GlobalTransactional  // 👈 只加这个注解
public void purchase(String userId, String commodityCode, int count) {
    orderService.create(userId, commodityCode, count);   // 订单服务
    stockService.deduct(commodityCode, count);            // 库存服务
    accountService.debit(userId, totalAmount);            // 账户服务
}

Seata 在 SQL 执行前后自动拦截:

-- 业务SQL:扣减库存
UPDATE stock SET count = count - 10 WHERE commodity_code = 'C001';

-- Seata 自动做的事:
-- 1. 执行前:查询修改前的数据(before image)
SELECT count FROM stock WHERE commodity_code = 'C001';  -- before: 100

-- 2. 执行业务SQL

-- 3. 执行后:查询修改后的数据(after image)
SELECT count FROM stock WHERE commodity_code = 'C001';  -- after: 90

-- 4. 生成 undo_log 记录
INSERT INTO undo_log (branch_id, xid, rollback_info) VALUES (
    1, 'xxx', 
    '{"beforeImage":{"count":100}, "afterImage":{"count":90}, "sqlType":"UPDATE"}' 👈
);

-- 5. 提交本地事务(业务数据 + undo_log 一起提交)
COMMIT;

关键点:本地事务直接提交了!不用等其他服务。这就是 AT 模式高性能的原因。

AT 模式:二阶段

提交:因为一阶段已经提交了,二阶段只需删除 undo_log。

回滚:读取 undo_log 中的 before image,生成反向 SQL 回滚:

-- undo_log 中的 before image: count = 100
-- 生成反向SQL:
UPDATE stock SET count = 100 WHERE commodity_code = 'C001';  -- 👈 恢复原值

-- 删除 undo_log
DELETE FROM undo_log WHERE branch_id = 1 AND xid = 'xxx';

全局锁:防止脏写。一阶段提交后,其他全局事务要修改同一行数据,必须等当前事务提交或回滚后才能获取全局锁。

全局锁机制

事务A:扣库存 count=100→90,获得全局锁
事务B:也想扣库存,发现全局锁被A持有
  → 事务B等待(不是阻塞,是自旋重试) 👈
事务A提交 → 释放全局锁
事务B获得全局锁 → 继续执行

AT vs TCC vs Saga

| 对比 | AT | TCC | Saga ||
| ------|-----|-----|------||
| 侵入性 | 零(只加注解) | 高(三个接口) | 中(正向+补偿) ||
| 一致性 | 最终一致 | 最终一致 | 最终一致 ||
| 性能 | 高(一阶段提交) | 高 | 最高 ||
| 隔离性 | 全局锁(读已提交) | 业务隔离 | 无隔离 ||
| 适用 | 常规业务 | 资金类 | 长流程 ||

Seata 全景

核心角色
├── TC —— 事务协调者(Seata Server)
├── TM —— 事务管理器(发起方)
└── RM —— 资源管理器(参与方)

AT模式流程
├── 一阶段 → 拦截SQL + before/after image + undo_log + 本地提交
├── 二阶段提交 → 删除undo_log
└── 二阶段回滚 → 读undo_log + 生成反向SQL + 恢复数据

关键机制
├── undo_log → 回滚依据
├── 全局锁 → 防脏写
└── XID传播 → 跨服务事务关联

口诀:一阶段拦截SQL,前后快照存undo;
      本地事务直接提,不用等别人完成;
      二阶段提交删日志,回滚用反向SQL;
      全局锁防脏写,业务代码零改造

回答技巧与点评

标准回答:Seata 是阿里开源的分布式事务框架,核心组件有 TC(协调者)、TM(管理器)、RM(资源管理器)。AT 模式实现零侵入:一阶段拦截 SQL,记录修改前后的数据快照到 undo_log,直接提交本地事务;二阶段提交时删 undo_log,回滚时根据 undo_log 生成反向 SQL 恢复数据。通过全局锁防止脏写,通过 XID 传播关联跨服务事务。

加分回答

  1. undo_log 的脏检查:回滚时 Seata 会对比当前数据和 after image,如果不一致说明被其他事务修改了,需要人工介入。这是 AT 模式的安全兜底
  2. Seata 的性能优化:Seata 1.5+ 引入异步提交(二阶段异步删除 undo_log),减少同步等待时间。还支持读写隔离级别的配置,读操作可以不加全局锁
  3. Seata 的局限:AT 模式只支持关系型数据库(需要解析 SQL 生成 undo_log);全局锁是性能瓶颈(高并发场景下锁竞争严重);不支持嵌套全局事务

面试官点评

这道题考的是你对 主流分布式事务框架 的理解。最忌讳的回答是"Seata 就是加个 @GlobalTransactional"——面试官想听的是 AT 模式的底层原理:怎么拦截 SQL、undo_log 怎么用、全局锁怎么防脏写。能把一阶段二阶段流程讲清楚,再说出 undo_log 的脏检查机制,就是高分回答。

原文阅读


内容有帮助?点赞、收藏、关注三连!评论区等你 💪

Logo

一站式 AI 云服务平台

更多推荐