一、凌晨三点的报警电话

“订单重复创建!用户投诉炸了!”
去年双11零点17分,手机疯狂震动。监控大盘刺眼的红色曲线中,我盯着日志里那行Transaction silently rolled back because it has been marked as rollback-only,手心全是冷汗。

排查72小时后真相扎心:一个@Async注解+自调用,让事务在无人察觉中失效
今天,我把踩过的坑、熬过的夜、写过的检查清单,毫无保留摊开给你看。


二、高频失效场景实战拆解(附修复代码)

坑1:方法修饰符埋雷

//  事务失效!Spring AOP要求public方法
@Transactional
protected void updateStock(Long skuId) { ... } 

//  修复:改为public + 检查final/static修饰
@Transactional
public void updateStock(Long skuId) { ... }

原理:JDK动态代理仅拦截public方法;CGLIB虽可代理非public,但Spring默认不启用且存在兼容风险。

坑2:this自调用陷阱(高频背锅点!)

@Service
public class OrderService {
    @Transactional
    public void createOrder(Order order) {
        //  事务失效!this调用绕过代理
        this.deductStock(order); 
    }
    
    @Transactional
    public void deductStock(Order order) { ... }
}

救命方案

// 方案1:注入自身(推荐)
@Autowired private OrderService self;
self.deductStock(order); 

// 方案2:AopContext.currentProxy()(需@EnableAspectJAutoProxy(exposeProxy=true))
((OrderService) AopContext.currentProxy()).deductStock(order);

️ 坑3:异常被捕获“静默死亡”

@Transactional
public void pay(Order order) {
    try {
        accountService.debit(order); // 可能抛BusinessException
    } catch (Exception e) {
        log.error("扣款异常", e); //  事务不会回滚!
        throw new RuntimeException(e); //  必须显式抛出
    }
}

关键配置

// 明确指定回滚异常类型(默认仅RuntimeException/Error回滚)
@Transactional(rollbackFor = Exception.class)

坑4:多数据源“张冠李戴”

//  未指定事务管理器,可能绑定到错误数据源
@Transactional 
public void syncData() { ... }

//  显式指定(结合@Primary合理规划)
@Transactional(transactionManager = "orderTransactionManager")

血泪建议:多数据源项目务必在启动日志中搜索Transaction manager确认绑定关系!

坑5:@Async与事务的“生死时速”

//  异步方法内开启新线程,原事务上下文丢失
@Async
@Transactional
public void sendNotify(Order order) { ... } // 事务失效!

正确姿势

  1. 主流程提交后触发异步(如ApplicationEvent)
  2. 异步方法内独立开启新事务(@Transactional(propagation = Propagation.REQUIRES_NEW)

其他高频坑速览

坑位现象一句话解法
坑6MyISAM表操作无回滚检查MySQL引擎:SHOW TABLE STATUS
坑7传播行为误用(如REQUIRES_NEW嵌套)画事务边界图!避免不必要嵌套
坑8事务超时设置过短@Transactional(timeout=30) 结合慢SQL优化
坑9测试环境H2内存库事务行为差异用Testcontainers跑真实DB测试
坑10事务方法被final/static修饰编译期检查:IDEA安装"Spring Transaction Check"插件

三、我的生产级防御清单(已落地验证)

  1. 启动时扫描

    @Component
    public class TransactionChecker implements CommandLineRunner {
        @Override
        public void run(String... args) {
            // 扫描@Transactional注解方法,校验修饰符/异常配置
            // 输出报告至日志,阻断启动(关键系统必备)
        }
    }
    
  2. 日志埋点

    # logback-spring.xml
    <logger name="org.springframework.transaction.interceptor" level="TRACE"/>
    

    关键:观察Getting transaction for [...]Completing transaction for [...]日志链

  3. 压测验证

    • 模拟异常场景,验证数据一致性
    • 检查慢事务:Arthas监控@Transactional方法耗时

四、灵魂总结

去年故障复盘会上,我贴出这张检查清单后,团队再未因事务问题深夜报警。技术人的成长,往往藏在这些“本可以避免”的细节里。


互动时间

 你在事务上栽过最深的跟头是什么?

本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:alixiixcom@163.com