MySQLTransactionRollbackException:lock wait timeout exceeded

文章描述了一次由于事务处理不当导致的MySQLTransactionRollbackException,主要问题是事务1删除B表数据后未提交,而新的事务2在同一表上尝试写操作,引发锁等待超时。解决方案包括避免使用多线程或改变事务的传播属性,如将BService的methodB事务设置为Propogation.REQUIRES_NEW,使其在新事务中运行,确保事务1及时提交。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

记一次Mysql异常:

MySQLTransactionRollbackException:lock wait timeout exceeded;try restarting transaction

排查方式:

去掉spring的事务再执行,如果不报这个错误,证明就是事务引起的;

产生问题的伪代码如下,可以看出问题产生的原因:

//controller中调用service的方法,为methodA开启了一个事务,假设叫 “事务1”
@Controller
public class AController{
    
    public void  a(){
        aService.methodA();
    }
}

//methodA 中删除B表数据,
public class AService{
    
    @Transaction
    public  void  methodA(){
        /*
         删除B表中的数据,"事务1" 已经获取了B表的写锁,
         但是注意:"事务1" 删除完数据(methodB()执行完后),并没有提交 “事务1”, 问题就出在这里
        */
        bService.methodB();
        
        /*
        开启一个新线程调用methodC, 由于methodC处于另一个线程中,而spring的事务是和线程绑定的,
        所以spring会为methodC开启另一个事务,假设叫 “事务2”
        */
       Thread thread = new Thread(new Runnable(){
            public void run(){
                /*
                    “事务2”中也要对B进行写操作,即也要获取B表的写锁,但此时写锁还被 “事务1”持有,
                     因此methodC 无法执行,一直卡住直到超时。

                     超时时间由变量 “innodb_lock_wait_timeout”进行配置,可以通过
                     show  varibales like 'innodb_lock_wait_timeout' 进行查看,单位为秒
                */
                cService.methodC();
            }
        })
        thread.start();
        
       //阻塞主线程直到
        thread.join();
    }
}

public class BService{
    @Transaction
    public  void  methodB(){
        //删除B表中的数据
        执行 delete from b where .....
    }
}

public class CService{
    @Transaction
    public  void  methodC(){
        //修改 B表中的数据
        执行 update b set  .....
    }
}

仔细看上面代码中的注释,分析过程:

① 主线程开启了一个事务1,对b表进行写操作,写完之后事务1未提交;

② 主线程开启子线程,且 子线程不执行完,主线程一直挂起(即join的作用)

③ spring的事务是通过线程进行传递的,子线程检测不到已经开启了事务1,会开启新事务,即事务2,准备对b表进行写操作

④ 由于b表写锁还被 事务1持有,所以事务2无法获取b表写锁,事务2一直等待,直到超时

总结就是:事务存在了嵌套(两个线程各开启了一个事务),外层事务对b表进行写操作,未释放锁,内层事务也要获取写锁操作b表,无法获取,一直等待。

事务1 开始

执行 delete from b ....... ,持有b表写锁

事务2开始

执行 update b set ...... , 获取b表写锁超时

事务2结束

事务1 结束

解决办法目前想到两种:

  1. 不用多线程,这样就不会开启事务2,就不会造成锁等待

  1. 如果你的业务场景允许 事务1 先提交,那么先提交事务1即可,即将 BService的传播途径改为Propogation.REQUIRES_NEW,表示处于一个新事务中,methodB()结束,事务1就提交了,事务2就可以正常获取b表写锁:

public class BService{
    @Transaction(propogation=Propogation.REQUIRES_NEW)
    public  void  methodB(){
        //删除B表中的数据
        执行 delete from b where .....
    }
}

如果有其他更好方式,请留言告知。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值