首页 > 数据库 >MySQL更新锁表超时 Lock wait timeout exceeded

MySQL更新锁表超时 Lock wait timeout exceeded

时间:2022-08-16 09:00:57浏览次数:83  
标签:状态 锁表 MySQL 更新 订单 添加 exceeded id 入库

背景

最近在做一个订单的钉钉审批功能,钉钉审批通过之后,订单更新审核状态,然后添加一条付款,并且更新付款状态:

// 订单审批通过
@Transactional(rollbackFor = Exception.class)
	public void orderPass() {
		// 更新订单审核状态
		updateOrderAuditStatus(id);
		// 添加入库
		addPutInStorage(id);
		// 更新订单入库状态
		updateOrderStorageStatus(id);
	}

其中的添加入库是远程ERP入库,添加出库之后更新出库状态。因为ERP可能因为库存不足,会入库失败。但此时审批流程已经结束,不可能再发起一遍审批流程。当添加入库失败订单审核状态正常更新,添加入库更新入库状态失败。这里的解决方案是:

拆分成两个方法,一个是更新订单审核状态,另一个添加入库和更新入库状态。添加入库和更新入库状态开启一个事务,也就是添加嵌套事务 REQUIRES_NEW,REQUIRES_NEW表示无论是否有事务,都会创建一个新的事务。

修改后的代码如下:

// 订单审批通过
@Transactional(rollbackFor = Exception.class)
public void orderPass() {
    // 更新订单审核状态
    updateOrderAuditStatus(id);
    try {
        // 更新出库  
        updatePutInStorage(id);
    } catch (Exception e) {
        System.out.println("更新出库失败");
    }

}

// 更新出库
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
public void updatePutInStorage(Long id) throws Exception{
    // 添加入库
    addPutInStorage(id);
    // 更新订单入库状态
    updateOrderStorageStatus(id);
    System.out.println("更新出库成功");
}

上面讲代码拆分成更新订单审核状态更新入库,其中更新入库报错会被try catch异常捕获,不会影响到订单审核状态更新。而添加入库更新订单入库状态处于同一个事务下,要么同时成功,要么同时失败。上述问题也解决了。

然而运行结果

com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction

原因分析

锁超时了,为什么会有锁呢?主要是这里添加了REQUIRES_NEW

  • 外层事务对表的更新锁住了表的行,外层事务还没有提交,就调用了内层事务updatePutInStorage,内层事务调用了updatePutInStorage
  • updatePutInStorage需要更新订单的入库状态,此时外层事务锁住了该表,所以更新订单的入库状态无法更新。
  • 更新订单的入库状态等待更新订单的审核状态,而REQUIRES_NEW又会让更新订单的审核状态等待更新订单的入库状态。造成相互等待,也就造成死锁

解决方案

死锁:两个线程为了保护两个不同的共享资源而使用了两个互斥锁,那么这两个互斥锁应用不当的时候,可能会造成两个线程都在等待对方释放锁,在没有外力的作用下,这些线程会一直相互等待,就没办法继续运行,这种情况就是发生了死锁。

上面锁超时原因,就是死锁的一种原因。所以需要把更新订单审核状态方法放在最后:

// 订单审批通过
@Transactional(rollbackFor = Exception.class)
public void orderPass() {
    
    try {
        // 更新出库  
        updatePutInStorage(id);
    } catch (Exception e) {
        System.out.println("更新出库失败");
    }
    // 更新订单审核状态
    updateOrderAuditStatus(id);

}

// 更新出库
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
public void updatePutInStorage(Long id) throws Exception{
    // 添加入库
    addPutInStorage(id);
    // 更新订单入库状态
    updateOrderStorageStatus(id);
    System.out.println("更新出库成功");
}

总结

  • 添加嵌套事务需要考虑到死锁的问题。
  • 一个事务只有等全部方法执行完毕之后才会提交事务。
  • 含有嵌套的事务的更新,需要按照相同的顺序更新,不然可能会出现锁相互等待的情况。

参考

业务上第一次遇到MySQL更新锁表超时( Lock wait timeout exceeded; try restarting transaction)

标签:状态,锁表,MySQL,更新,订单,添加,exceeded,id,入库
From: https://www.cnblogs.com/jeremylai7/p/16590365.html

相关文章

  • MySql--存储过程
    MySql--存储过程创建DELIMITER$$--代表执行分隔符(原来是;这里重新定义为$$)USE`furn_ssm`$$--存储过程建立在数据库下,这里指使用数据库furn_ssmDROPPROCEDURE......
  • mysql和navicat的安装和使用
    昨天把Pycharm安装好了,今天开始安装mysql 数据库。MySql如果电脑上第一次安装mysql,会让注册一个Oracle帐户,浏览器输入:mysqlforwindows 就可以找到,新版本......
  • mysql使用support-files下的mysql.server启动报错“Starting MySQL ERROR! Couldn't f
    报错版本:mysql-5.7.351、报错完整提示信息;[root@localhostsupport-files]#./mysql.serverstart./mysql.server:line239:my_print_defaults:commandnotfound......
  • MySQL
    字符编码与配置文件#由于5.6版本编码不统一会造成乱码我们需要把它统一修改成》:utf81.MySQL里的my-default.ini是windows下MySQL默认的配置文件2.拷贝文件并重新命......
  • centos/windows服务器,Mysql数据库表结构损坏-已解决
    【问题原因】服务器突然断电【故障报告】数据库表结构损坏【解决思路】进入强制恢复模式,备份库表及数据重建故障发现周末公司断电,周一启动数据库就直接报错了查看日......
  • mysql数据库-2
    目录字符编码与配置文件存储引擎创建表的完整语法字段类型之整型字段类型之浮点型字段类型之字符类型数字的含义字段类型之枚举与集合字段类型之日期类型字段约束条件字符......
  • 【2022.8.15】MySQL数据库(2)
    今日内容概要字符编码与配置文件数据库存储引擎创建表的完整语法MySQL字段类型MySQL字段约束今日内容详细字符编码与配置文件如何查看数据库基本信息(用户......
  • MySQL字段类型
    今日内容字符编码与配置文件查看数据库基本信息(用户字符编码) \swindows下MySQL默认的配置文件(苹果本不一样) my-default.int 拷贝上述文件并且重命名为my.ini由......
  • MySQL字段类型、字符编码与配置文件
    目录字符编码与配置文件存储引擎创建表的完整语法字段类型之整形字段类型之浮点型字段类型之字符类型字段后面的含义字段类型之枚举和集合字段类型之日期类型字段约束条件......
  • 2022-08-15 第六小组 张宁杰 Mysql数据库(1)
    Mysql数据库数据库数据库【按照数据结构来组织、存储和管理数据的仓库】。是一个长期存储在计算机内的、有组织的、可共享的、统一管理的大量数据的集合。数据对于公司......