记一次Spring @Transactional失效的排查过程

问题

压力测试时发现生成了相同的序号,根据日志分析发现select ... for update没有锁住某一行的数据,从而致使序号重复java

排查

  1. 十分肯定select ... for update的互斥机制,该行数据一定被锁,其余select ... for update/update/delete等SQL须要等待锁才能执行后续操做.
  2. 首先怀疑事务是否起做用,使用TransactionSynchronizationManager#getCurrentTransactionName打印当前的事务,结果居然是null!!!没有事务天然不会互斥, 能解释为何生成重复序号了.
  3. 剩下就是为何明明加了@Transactional注解,可是没有开启事务了.
  4. 带着问题检查代码,有问题的代码以下图.这是一个责任链的设计模式,对于责任链的任一个节点,先调用doProcess执行子类自身的逻辑,若是成功则执行下一个succ节点,报错则执行下一个fail节点,直到责任链结束.
  5. 找到了声明事务的doProcess方法,他是在process里用this.doProcess调起的,咱们都知道Spring的AOP事务要生效,必须使用 另外一对象.方法 的形式才有效,这就是@Transactional失效的缘由. 简而言之,不能在同一个类中使用没有@Transactional的方法调起有@Transactional的方法
public abstract class BaseController {
    private ScBaseController succNext = null;
    private ScBaseController failNext = null;

    public DataMessage process(DataMessage msg) throws ScBaseException {
        DataMessage respMsg = this.doProcess(msg);
        // 执行成功
        if (ExecuteResultEnum.SUCCESS.equals(respMsg.getExecuteResult())) {
            if (this.succNext != null) {
                return this.succNext.process(respMsg);
            } else {
                return respMsg;
            }
        }
        // 执行失败
        else {
            if (this.failNext != null) {
                return this.failNext.process(respMsg);
            } else {
                return respMsg;
            }
        }
    }
    
    @Transactional     // 这里的事务注解没有做用
    public abstract DataMessage doProcess(DataMessage msg);
复制代码

教训

  1. 看过不少文章都说过不能在同一个类中使用没有@Transactional的方法调起有@Transactional的方法,这个是Spring动态AOP的机制决定的,必须紧记这个教训.至于为何动态AOP会这样,后续若是写到这方面的文章再详细说明.设计模式

  2. 幸亏在压力测试中发现这个问题,并无形成线上故障.有些问题不是作几笔交易就能说明清楚,必须在高并发高压力的测试下才能验证系统的健壮性.并发

相关文章
相关标签/搜索