【Spring深入】一、事务

1、Spring事务

1.1 事务开启类型

  • REQUIRED (默认) 如果已存在事务,则加入该事务,否则新建一个事务
  • REQUIRED_NEW 每次都是新建一个事务,上一个事务挂起
  • SUPPORTS 支持当前事务,如果没有则不创建事务
  • NOT_SUPPORTED 以非事务的方式执行,如果当前存在事务,则挂起
  • MANDATORY 必须在一个已有事务中运行,否则抛异常。
  • NEVER 不能在事务中运行,否则抛异常。
  • NESTED 如果当前存在事务,则在嵌套事务(SavePoint,可回滚子事务,不影响主事务)内执行;否则行为类似 REQUIRED。

1.2 事务原理

  • 基于AOP,通过代理模式,JDK代理或者CGLIB代理
  • 通过拦截器开启事务,提交或者回滚
  • 通过try catch 方式,在catch块中回滚事务
  • 通过ThreadLocal记录当前线程的事务,当前线程是否开启事务,当前线程持有的数据源对应的数据库连接

1.3 事务失效

  • 在一个未开启事务的方法中调用了本对象中一个开启事务的方法,不会走代理
  • 捕获异常,导致无法回滚
  • 在非public的方法上使用@Transactional
  • 抛出异常不匹配@Transactional设定的异常
  • 在方法中使用异步操作

2、分布式事务

2.1 分布式事务的种类

  • 2PC,两阶段提交,其实就是利用本地事务,对修改行开启事务,然后执行完了,协调者向所有参与者发送提交指令,强一致性,但是性能很差,如果哪一个方法有异常,则统一回滚,如果协调者有问题,就会导致事务一直挂起
  • 3PC,相对于2PC增加了预校验,看能不能执行该操作,但是和2PC一样,同样开启本地事务,强一致性,但是性能很差
  • TCC try, confirm, concel,通过预先执行操作,比如转账操作,进行一些不被用户看到的数据库表变更,好处是,所有的操作都是直接执行提交,不会有锁竞争,所以高性能,而且也满足最终一致性,缺点是需要自己实现try confirm concel操作,而且会出现悬挂,空回滚问题,confirm超时问题
  • Saga 长事务模式,每一步的变更都有对应的回退操作,不管哪一步失败,都从失败的地方逐步回退,但是问题是变更状态会被用户直接看到,而且会存在无法回滚的情况,最终一致性
  • 本地消息表模式/MQ模式,在完成某一步操作后,同步插入一条数据到本地消息表,然后通过MQ的方式进行处理,最终一致性
  • seata的AT模式,在每个数据库设置一个属于seata的undo log表,用于回滚真提交事务之后的操作,只能用于数据库,最终一致性
  • seata的XA模式,强一致性,和2PC类似

2.2 TCC的问题

  • Seata 等 TCC 框架内部已经通过 xid + 分支 ID 的方式,在 TC 侧做了幂等控制,但业务开发者依然需要保证自己 Confirm/Cancel 方法的业务逻辑是幂等的,这是双重保险。
  • 每个服务必须记录阶段的状态!
    在方法中,先插入一条事务记录到本地数据库(如 tcc_transaction 表),通过状态来进行幂等处理
问题 原因 解决方案
空回滚 CancelTry 之前到达 Cancel 时检查 Try 是否执行过,没执行过就跳过。
悬挂 Cancel 先执行成功,Try 后到达并执行 Try 时检查是否已被 Cancel,如果已被取消就拒绝执行。
Confirm/Cance 超时 网络问题导致 TM 不确定操作是否成功 确保 ConfirmCancel 方法是幂等的,允许被重复调用。

2.3 Saga和MQ如何做到有迹可循

特性 Saga (Orchestration) MQ (本地消息表)
"迹"的载体 中心化的 Saga 状态机实例表 (saga_id, current_state, history) 去中心化的本地消息表 (message_id, content, status)
追踪方式 通过 saga_id 可以完整还原整个长事务的执行路径。 通过 message_id 只能知道单个消息的生产和消费状态,跨服务的完整链路需要额外的链路追踪(如 SkyWalking)。
协调者 有明确的中心化编排器,负责驱动流程和补偿。 无中心协调者,靠消息的发布/订阅自然驱动。
适用场景 复杂的、多步骤的、需要精确补偿的业务流程(如订单创建、机票预订)。 简单的、点对点的、事件驱动的最终一致性(如用户注册后发欢迎邮件、积分变更)。
  • Saga每次执行新的长事务,会生成一个唯一的saga_id,并且插入到数据库中
  • 同一个长事务,执行到不同阶段,对应的记录也会变更状态
问题 答案
记录执行进度是 INSERT 还是 UPDATE? 首次启动时 INSERT 一条实例记录,后续所有进度更新都是 UPDATE 这条记录。
"执行到哪一步"的状态是用户自定义的吗? 状态的名称(如 "扣库存")由用户在状态机定义文件中自定义,但状态的流转和持久化由框架自动管理。

2.4 分布式事务的要求

  • CAP
  • C considency 一致性
  • A abilty 高可用
  • P Partition 网络分区
  • 其中P是必定会有问题的,所以只能满足CA
分布式事务模式 一致性 © 可用性 (A) 网络分区容忍 § CAP 归属 说明
2PC / 3PC / XA ✅ 强一致性 ❌ 低可用性 CP 协调者或任一参与者宕机/网络分区,整个事务会阻塞挂起,无法对外提供服务(不可用)。但一旦成功,数据绝对一致。
TCC ⚠️ 最终一致性 ✅ 高可用性 AP Try 阶段成功后,资源已被预留,即使后续 Confirm/Cancel 因网络问题失败,系统也是可用的(用户能看到"处理中"状态)。通过后台任务重试,最终能达到一致。牺牲了强一致性,换取了可用性。
Saga ⚠️ 最终一致性 ✅ 高可用性 AP 每个子事务都是独立提交的。即使中间某步因网络问题失败,前面的步骤已经生效(用户可见),系统依然可用。通过执行补偿事务来最终达到一致。典型的 AP 系统。
本地消息表 / MQ 事务消息 ⚠️ 最终一致性 ✅ 高可用性 AP 生产者发消息和本地事务在一个库,保证了原子性。消费者可能因网络问题暂时收不到消息,但消息队列会重试,最终会被消费。整个链路是最终一致且高可用的。
Seata AT ⚠️ 最终一致性 ✅ 高可用性 AP 虽然看起来像本地事务,但它依赖 TC(协调者)。如果 TC 宕机,新的全局事务无法开启,但已经提交的本地事务不受影响,数据库依然可用。回滚依赖异步的 undo_log 清理。设计目标是高性能和高可用,接受最终一致。
相关推荐
七老板的blog9 小时前
当 Spring StateMachine 遇见大模型:构建工业级 AI 写作流水线
java·人工智能·spring
云烟成雨TD9 小时前
Spring AI 1.x 系列【46】MCP Security 模块
java·人工智能·spring
小旭952710 小时前
Spring AI Alibaba 从入门到实战:一站式掌握企业级 AI 应用开发
java·人工智能·spring
云烟成雨TD12 小时前
Spring AI 1.x 系列【50】可观测性:接入 Prometheus + Grafana
人工智能·spring·prometheus
phltxy13 小时前
MCP 从协议到 Spring AI 实战
人工智能·spring·oracle
Volunteer Technology15 小时前
SpringSecurity请求流转的本质
java·spring
云烟成雨TD17 小时前
Spring AI 1.x 系列【42】MCP 服务端 Spring Boot 启动器
java·人工智能·spring
云烟成雨TD17 小时前
Spring AI 1.x 系列【38】模型上下文协议(MCP)
java·人工智能·spring
Alson_Code17 小时前
Spring AI-1.1.0
java·人工智能·后端·spring·ai编程
小小放舟、17 小时前
@JsonCreator 注解详解——从枚举反序列化说起
spring boot·spring·spring cloud·java-ee·maven·intellij-idea·状态模式