seata--重点笔记

一 2PC协议

(一)2PC流程

2PC(两阶段提交,Two-Phase Commit)
顾名思义,分为两个阶段:Prepare 和 Commit
Prepare:提交事务请求
基本流程如下图:

image

1. 询问协调者向所有参与者发送事务请求,询问是否可执行事务操作,然后等待各个参与者的响应。
2. 执行各个参与者接收到协调者事务请求后,执行事务操作(例如更新一个关系型数据库表中的记录),并将Undo 和 Redo 信息记录事务日志中。
3. 响应如果参与者成功执行了事务并写入Undo 和 Redo 信息,则向协调者返回 YES 响应,否则返回 NO响应。当然,参与者也可能宕机,从而不会返回响应。

 

 
Commit:执行事务提交
执行事务提交分为两种情况,正常提交和回退。 
(1)正常提交事务
流程如下图: 

image

1. commit 请求 协调者向所有参与者发送 Commit 请求。
2. 事务提交 参与者收到 Commit 请求后,执行事务提交,提交完成后释放事务执行期占用的所有资源。
3. 反馈结果 参与者执行事务提交后向协调者发送 Ack 响应。
4. 完成事务 接收到所有参与者的 Ack 响应后,完成事务提交。

 

(2)中断事务
在执行 Prepare 步骤过程中,如果某些参与者执行事务失败、宕机或与协调者之间的网络中断,那么协调者就无法
收到所有参与者的 YES 响应,或者某个参与者返回了 No 响应,此时,协调者就会进入回退流程,对事务进行回
退。流程如下图红色部分(将 Commit 请求替换为红色的 Rollback 请求):

image

1. rollback 请求 协调者向所有参与者发送 Rollback 请求。
2. 事务回滚 参与者收到 Rollback 后,使用 Prepare 阶段的 Undo 日志执行事务回滚,完成后释放事务执行期占用的所有资源。
3. 反馈结果 参与者执行事务回滚后向协调者发送 Ack 响应。
4. 中断事务 接收到所有参与者的 Ack 响应后,完成事务中断。

 

(二)2PC 的问题

1. 同步阻塞 参与者在等待协调者的指令时,其实是在等待其他参与者的响应,在此过程中,参与者是无法进行其他操作的,也就是阻塞了其运行。
倘若参与者与协调者之间网络异常导致参与者一直收不到协调者信息,那么会导致参与者一直阻塞下去。
2. 单点 在 2PC 中,一切请求都来自协调者,所以协调者的地位是至关重要的,如果协调者宕机,那么就会使参与者一直阻塞并一直占用事务资源。
如果协调者也是分布式,使用选主方式提供服务,那么在一个协调者挂掉后,可以选取另一个协调者继续后续的服务,可以解决单点问题。
但是,新协调者无法知道上一个事务的全部状态信息(例如已等待 Prepare 响应的时长等),所以也无法顺利处理上一个事务。
3. 数据不一致 Commit 事务过程中 Commit 请求/Rollback 请求可能因为协调者宕机或协调者与参与者网络问题丢失,那么就导致了部分参与者没有收到 Commit/Rollback 请求,
而其他参与者则正常收到执行了Commit/Rollback 操作,没有收到请求的参与者则继续阻塞。这时,参与者之间的数据就不再一致了。当参与者执行 Commit/Rollback 后会向协调者发送 Ack,
然而协调者不论是否收到所有的参与者的 Ack,该事务也不会再有其他补救措施了,协调者能做的也就是等待超时后像事务发起者返回一个“我不确定该事务是否成功”。
4. 环境可靠性依赖 协调者 Prepare 请求发出后,等待响应,然而如果有参与者宕机或与协调者之间的网络中断,都会导致协调者无法收到所有参与者的响应,
那么在 2PC 中,协调者会等待一定时间,然后超时后,会触发事务中断,在这个过程中,协调者和所有其他参与者都是出于阻塞的。这种机制对网络问题常见的现
实环境来说太苛刻了。

 

 

二 AT模式(auto transcation) 

AT 模式是一种无侵入的分布式事务解决方案。
阿里seata框架,实现了该模式。
在 AT 模式下,用户只需关注自己的“业务 SQL”,用户的 “业务 SQL” 作为一阶段,Seata 框架会自动生成事务的二阶段提交和回滚操作。 

image

 

AT 模式如何做到对业务的无侵入 : 
一阶段

 

在一阶段,Seata 会拦截“业务 SQL”,首先解析 SQL 语义,找到“业务 SQL”要更新的业务数据,在业务数据被更新前,将其保存成“before image”,然后执行“业务 SQL”更新业务数据,

 

在业务数据更新之后,再将其保存成“after image”,最后生成行锁。以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性。 

 

image

 

二阶段提交:
二阶段如果是提交的话,因为“业务 SQL”在一阶段已经提交至数据库, 所以 Seata 框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可

image

 

二阶段回滚: 
二阶段如果是回滚的话,Seata 就需要回滚一阶段已经执行的“业务 SQL”,还原业务数据。回滚方式便是用“before image”还原业务数据;但在还原前要首先要校验脏写,对比“数据库当前业务
数据”和 “after image”,如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理。
(判定脏写冲突,自动回滚终止,抛出 UndoLogDirtyException,记录异常日志,等待人工介入处理,后面有人工处理的步骤)

image

 

AT 模式的一阶段、二阶段提交和回滚均由 Seata 框架自动生成,用户只需编写“业务SQL”,便能轻松接入分布式事务,AT 模式是一种对业务无任何侵入的分布式事务解决方案。 

 

三 TCC 模式 

1. 侵入性比较强, 并且得自己实现相关事务控制逻辑
2.在整个过程基本没有锁,性能更强
TCC 模式需要用户根据自己的业务场景实现 Try、Confirm 和 Cancel 三个操作;事务发起方在一阶段执行 Try 方式,在二阶段提交执行 Confirm 方法,二阶段回滚执行 Cancel 方法。

 

image

 

TCC讲解

 

TCC2

 

TCC 三个方法描述: 
Try:资源的检测和预留;
Confirm:执行的业务操作提交;要求 Try 成功 Confirm 一定要能成功;
Cancel:预留资源释放;
TCC 的实践经验
蚂蚁金服TCC实践,总结以下注意事项:
➢业务模型分2阶段设计
➢并发控制
➢允许空回滚
➢防悬挂控制
➢幂等控制

四 saga模式

image

saga模式的实现,是长事务解决方案。
Saga 是一种补偿协议,在 Saga 模式下,分布式事务内有多个参与者,每一个参与者都是一个冲正补偿服务,需要用户根据业务场景实现其正向操作逆向回滚操作。 
Saga模式的优势是:
一阶段提交本地数据库事务,无锁,高性能;
参与者可以采用事务驱动异步执行,高吞吐;
补偿服务即正向服务的“反向”,易于理解,易于实现;
缺点:Saga 模式由于一阶段已经提交本地数据库事务,且没有进行“预留”动作,所以不能保证隔离性。后续会讲到对于缺乏隔离性的应对措施。
与TCC实践经验相同的是,Saga 模式中,每个事务参与者的冲正、逆向操作,需要支持:
空补偿:逆向操作早于正向操作时;
防悬挂控制:空补偿后要拒绝正向操作
幂等 

 

 

五(AT、TCC、Saga、XA)模式分析

四种分布式事务模式,分别在不同的时间被提出,每种模式都有它的适用场景
AT 模式是无侵入的分布式事务解决方案,适用于不希望对业务进行改造的场景,几乎0学
习成本。
TCC 模式是高性能分布式事务解决方案,适用于核心系统等对性能有很高要求的场景。
Saga 模式是长事务解决方案,适用于业务流程长且需要保证事务最终一致性的业务系统,
Saga 模式一阶段就会提交本地事务,无锁,长流程情况下可以保证性能,多用于渠道层、集成
层业务系统。事务参与者可能是其它公司的服务或者是遗留系统的服务,无法进行改造和提供
TCC 要求的接口,也可以使用 Saga 模式。
XA模式是分布式强一致性的解决方案,但性能低而使用较少。 

 

 

 

 

六 Seata的三大角色

在 Seata 的架构中,一共有三个角色:
TC (Transaction Coordinator) - 事务协调者
维护全局和分支事务的状态,驱动全局事务提交或回滚。
TM (Transaction Manager) - 事务管理器
定义全局事务的范围:开始全局事务、提交或回滚全局事务。
RM (Resource Manager) - 资源管理器
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
其中,TC 为单独部署的 Server 服务端,TM 和 RM 为嵌入到应用中的 Client 客户端

 

在 Seata 中,一个分布式事务的生命周期如下:

 

image

1.TM 请求 TC 开启一个全局事务。TC 会生成一个 XID 作为该全局事务的编号。XID,会在微服务的调用链路中传播,保证将多个微服务
的子事务关联在一起。当一进入事务方法中就会生成XID , global_table 就是存储的全局事务信息 ,
2.RM 请求 TC 将本地事务注册为全局事务的分支事务,通过全局事务的 XID 进行关联。当运行数据库操作方法,branch_table 存储事务参与者
3.TM 请求 TC 告诉 XID 对应的全局事务是进行提交还是回滚。
4.TC 驱动 RM 们将 XID 对应的自己的本地事务进行提交还是回滚。

image

 

 几个重要角色和中间件:nacos注册中心和配置管理中心,seata-server事务协调者,XID全局事务ID,undo log事务回滚日志

 

 

Seata 脏写抛异常后,标准人工处理完整流程

一、先明确触发脏写的根本原因

脏写本质:一阶段本地事务提交后、二阶段回滚前,非 Seata 事务 / 绕过全局锁的代码修改了该行数据(定时任务、手动 SQL、无 @GlobalTransactional 接口、其他系统直连 DB),导致当前数据 ≠ undo_log 里的 after image,Seata 无法自动判断该覆盖哪份数据,直接抛UndoLogDirtyException,停止自动回滚并告警。

二、人工处理标准五步(生产通用流程)

步骤 1:定位异常事务,拉全量快照信息

  1. 从日志拿到报错关键字段:XIDbranchId、表名、主键、报错堆栈;
  2. 查询当前库undo_log表,根据 XID 取出这条记录:
SELECT * FROM undo_log WHERE xid = '你的报错XID';

 

  1. 解析rollback_info二进制字段,得到三份核心数据:
    • before_image:一阶段执行 SQL 前的原始数据(理论上回滚目标值)
    • after_image:一阶段执行完后的数据(Seata 校验基准)
    • 原始业务 SQL(update/insert/delete)、主键、表名
  2. 查询业务表,拿到数据库当前真实数据
  3. 同时查 TC 的lock_table全局锁表,确认这条 XID 是否仍持有全局锁:
SELECT * FROM lock_table WHERE xid = '你的报错XID';

 

步骤 2:业务复盘,判断数据冲突场景(分 3 种情况)

场景 1:业务需求要求全局事务必须彻底回滚(主流场景)

全局事务整体失败,中间第三方修改属于错误操作,要把数据恢复到before_image状态。 操作:

  1. 手动执行逆向 SQL,用 before_image 覆盖当前业务数据;
    • 原 UPDATE:update 表 set 字段=before值 where 主键=xx
    • 原 INSERT:delete from 表 where 主键=xx
    • 原 DELETE:insert into 表(所有字段) values(before_image全部值)
  2. 核对业务上下游(订单、库存、账户流水),保证关联数据全部对齐;
  3. 数据校验无误后,删除这条阻塞的 undo_log(必须手动删,否则 Seata 会无限重试回滚):
DELETE FROM undo_log WHERE xid = '报错XID' AND branch_id = '分支ID';

 

  1. 清理 lock_table 中残留全局锁(避免锁占用阻塞新事务):
DELETE FROM lock_table WHERE xid = '报错XID';

 

场景 2:第三方修改是合法业务操作,全局事务放弃回滚

中间修改逻辑有效,不能覆盖,保留当前数据库最新数据,全局事务标记为 “人工完成”。 操作:

  1. 核对上下游业务,确认当前数据业务逻辑正确,不需要还原;
  2. 直接删除 undo_log 和 lock_table 对应记录,释放资源;
  3. 记录台账:XID、脏写原因、处理人、处理时间,避免对账不一致。

场景 3:两份数据都不对,需要业务人工修正(金融 / 订单高频)

比如账户余额、库存被多次交叉修改,before/after/current 三份快照都不符合业务预期。 操作:

  1. 拉取操作日志、业务流水、操作人记录,按业务时序重新计算正确值;
  2. DBA 执行修正 SQL,写入标准业务值;
  3. 删除 undo_log、清理全局锁;
  4. 事后对账,补流水记录,留存修正凭证。

步骤 3:清理阻塞资源(必做,否则无限重试)

脏写异常后 Seata 客户端会自动重试回滚(默认 3 次,间隔递增),不删 undo_log 会持续报错、占用全局锁阻塞新事务:

  1. 业务数据人工校准完成 → 删除 undo_log 对应分支记录;
  2. 删除 lock_table 里该 XID 的全局锁;
  3. 重启相关服务或等待重试周期结束,恢复正常流量。

步骤 4:对账校验(金融 / 电商强要求)

修正后必须全链路核对:

  • 本表单条记录数值;
  • 关联分支事务(订单、库存、账户);
  • 流水表、操作日志、变更记录; 确保分布式事务最终一致性,无单边脏数据。

步骤 5:根因修复,避免再次发生脏写(长效方案)

脏写大多是代码不规范导致,人工修复只是治标,必须修复源头:

  1. 所有修改业务表的接口统一加@GlobalTransactional,走 Seata 全局锁;
  2. 定时任务、后台脚本、第三方系统禁止直写业务库,统一走微服务接口;
  3. 开启@GlobalLock给纯查询 / 更新接口加全局锁隔离;
  4. 重要表增加乐观锁 version 字段,双重兜底;
  5. 关闭直连 DB 的后台管理写权限,统一走服务层代理。

三、关键注意事项(生产踩坑点)

  1. 严禁不修正数据直接删 undo_log:会造成分布式数据永久不一致;
  2. 不要关闭脏写校验(seata.undo.logValidation=none),生产默认 strict;
  3. 脏写告警建议对接短信 / 邮件,及时发现,不要等对账才暴露;
  4. 金融核心场景处理全程留痕,操作 SQL、快照、处理记录归档备查;
  5. TC 宕机、锁残留场景,必须清理 lock_table,否则后续事务全部阻塞。

极简总结人工处理口诀

  1. 拿 XID 查 undo_log,取出三张镜像;
  2. 梳理业务,确定正确目标数据;
  3. DBA 手动修正业务表;
  4. 删除 undo_log、清理全局锁;
  5. 全链路对账;
  6. 修复代码,杜绝再次脏写。

 

 

 

 

 

 
posted @ 2026-04-12 15:40  OMGq  阅读(14)  评论(0)    收藏  举报