AI分析不同阶层思维 二:Spring 的事务在什么情况下会失效?

这是我在线上事故复盘 里,最常被问、也最容易被误解的问题之一。

因为大多数人学的是"事务怎么用",

而事故发生时,考验的是:你是否真的知道事务是在什么时候、以什么方式生效的。


一、用一个真实例子,先把"事务失效 + 脏数据"跑出来(必须具体)

1️⃣ 业务场景:用户余额扣减 + 订单创建

这是一个非常真实的业务

  • 用户下单
  • 扣减用户余额
  • 创建订单
  • 任意一步失败,必须全部回滚

这个服务在 Spring 中是一个单例 Bean


2️⃣ 最小可理解示例代码(明确共享状态 + 事务位置)

java 复制代码
@Service
public class OrderService {

    // ⚠️ 单例 Bean 的成员变量(共享状态)
    private BigDecimal currentBalance;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private OrderMapper orderMapper;

    // 事务加在这里
    @Transactional
    public void createOrder(Long userId, BigDecimal amount) {
        // 1. 查询余额
        currentBalance = userMapper.queryBalance(userId);

        // 2. 扣减余额
        currentBalance = currentBalance.subtract(amount);
        userMapper.updateBalance(userId, currentBalance);

        // 3. 创建订单
        saveOrder(userId, amount);
    }

    // ⚠️ 注意:内部方法调用
    public void saveOrder(Long userId, BigDecimal amount) {
        // 模拟异常
        if (amount.compareTo(new BigDecimal("100")) > 0) {
            throw new RuntimeException("金额过大,创建订单失败");
        }
        orderMapper.insert(userId, amount);
    }
}

先别急着挑刺。

这段代码在大量真实项目中都出现过"变体"


3️⃣ 并发 + 事务时间线:问题是如何一步一步发生的

现在有两个并发请求:

  • 请求 A:用户 1,下单 80 元(正常)
  • 请求 B:用户 1,下单 200 元(会抛异常)
⏱ 时间线(关键在"你以为事务生效了")
复制代码
T1:线程 A 进入 createOrder()
    Spring 开启事务 Tx-A

T2:线程 B 进入 createOrder()
    Spring 开启事务 Tx-B

T3:线程 A 查询余额
    currentBalance = 500

T4:线程 B 查询余额
    currentBalance = 500   ← 覆盖 A 的中间态

T5:线程 A 扣减余额
    currentBalance = 500 - 80 = 420
    updateBalance(420)

T6:线程 B 扣减余额
    currentBalance = 420 - 200 = 220
    updateBalance(220)

T7:线程 A 调用 saveOrder()
    正常插入订单
    Tx-A 提交

T8:线程 B 调用 saveOrder()
    抛出 RuntimeException
    ❗ 但注意:事务并没有回滚用户余额

4️⃣ ❗关键点:事务为什么"没兜住"?

读到这里你必须明确三件事:

  1. @Transactional 确实生效了
  2. 异常 确实抛出了
  3. 但余额还是被改脏了

👉 这不是并发"概率问题",而是执行路径决定的"必然结果"。

为什么?

  • @Transactional 只对通过代理调用的方法生效

  • saveOrder()类内部调用

  • 事务边界没有覆盖你以为的执行范围

  • 再叠加:

    • 单例 Bean
    • 成员变量保存中间态
    • 并发请求

结果一定是:

数据被改了,事务没帮你兜底。

到这里,你应该已经能回答:

"原来 Spring 事务是这样一步一步失效的。"


二、1--2 万工程师如何看这个问题(功能交付视角)

这个阶段的人,看完上面的代码,反应通常是:

1️⃣ 第一反应:"事务不是加了吗?"

  • @Transactional
  • 抛的是 RuntimeException
  • 按文档:应该回滚

于是他们会开始:

  • 怀疑数据库
  • 怀疑隔离级别
  • 怀疑 Spring 版本

2️⃣ 常见修法

  • @Transactional 挪到别的方法
  • 把异常改成 RuntimeException
  • rollbackFor = Exception.class

3️⃣ 为什么他们会"以为问题解决了"

  • 单线程测试 OK
  • 本地跑流程 OK
  • 事务日志显示"rollback"

但他们忽略了一件事:

他们从来没用"并发 + 执行路径"去推演真实过程。


三、3--5 万工程师如何看这个问题(系统责任视角)

这个层级的人,看问题的方式已经完全不同。

1️⃣ 他们会立刻注意到的风险信号

  • 单例 Service
  • 成员变量保存请求态
  • 内部方法调用 + 事务
  • 把"业务正确性"寄托在事务注解上

2️⃣ 为什么他们不急着改注解

因为他们知道:

事务是兜底机制

不是业务隔离机制

3️⃣ 他们真正害怕的系统后果

  • 数据"部分正确、部分错误"
  • 财务类数据不可追溯
  • 问题无法复现

4️⃣ 为什么他们会否定"表面正确"的修法

在他们眼里:

  • 调整注解位置
  • 改异常类型
  • 加 rollbackFor

都只是:

让系统"看起来更安全",而不是"真的安全"


四、100 万级工程师 / 架构负责人怎么看(系统定价视角)

到这个层级,关注点已经彻底变了。

1️⃣ 他们还会讨论"事务怎么配吗"?

几乎不会。

他们问的是:

  • 为什么允许在 Service 里存请求态?
  • 为什么团队默认"事务 = 数据安全"?
  • 为什么没有并发执行推演?

2️⃣ 如果真出了事故,追责哪一层?

不是:

  • 写代码的人

而是:

  • 技术负责人
  • 代码评审机制
  • 架构约束是否失效

3️⃣ 在他们眼里,这是什么问题?

这暴露的不是 Spring 技术问题

而是:
团队是否理解"事务能力的边界"


五、为什么这是工程师身价差异的分水岭?

我们直接给结论:

层级 关注点
1--2 万 事务为什么没回滚
3--5 万 执行路径是否被事务覆盖
100 万 为什么业务正确性依赖事务

差距不在"会不会用 @Transactional",
而在于:

是否理解系统是如何在"看似安全"的情况下悄悄失控的。


六、给读者的认知校准

1️⃣ 最常见的认知陷阱

  • "事务能保证一致性"
  • "异常抛了就会回滚"
  • "Spring 都帮我处理好了"

这些话,
在事故复盘会上,几乎都是前奏。

2️⃣ 哪些能力不是靠多背规则获得的

  • 并发执行路径推演能力
  • 事务生效边界的直觉
  • 对"兜底机制"的不信任感

3️⃣ 一个自检问题(非常重要)

当你看到最开始那段代码时,你的第一反应是:

  • ❌「事务怎么没生效?」
  • ✅「为什么这个设计需要事务兜底?」

这个差异,

决定了你未来能不能继续往上走。


如果你愿意,下一步我可以继续拆:

  • 为什么"自调用"是事务失效的根本原因
  • 为什么很多人把事务当成"原子性保险"
  • 真实系统里,事务真正该负责什么、不该负责什么

你一句话,我继续往下拆。

相关推荐
元Y亨H15 小时前
Nacos - 服务发现
java·微服务
微露清风15 小时前
系统性学习C++-第十八讲-封装红黑树实现myset与mymap
java·c++·学习
dasi022715 小时前
Java趣闻
java
阿波罗尼亚16 小时前
Tcp SSE Utils
android·java·tcp/ip
susu108301891116 小时前
springboot3.5.8整合minio8.5.9
java·springboot
不知道累,只知道类16 小时前
深入理解 Java 虚拟线程 (Project Loom)
java·开发语言
myzshare16 小时前
实战分享:我是如何用SSM框架开发出一个完整项目的
java·mysql·spring cloud·微信小程序
Chan1617 小时前
【 Java八股文面试 | JavaSE篇 】
java·jvm·spring boot·面试·java-ee·八股
wen__xvn17 小时前
代码随想录算法训练营DAY10第五章 栈与队列part01
java·前端·算法