背景

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

// 订单审批通过
@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更新锁表超时 Lock wait timeout exceeded的更多相关文章

  1. Mysql事物锁等待超时(Lock wait timeout exceeded; try restarting transaction)

    一.问题描述 在做查询语句时,MySQL 抛出了这样的异常:锁等待超时 Lock wait timeout exceeded; try restarting transaction,是当前事务在等待其 ...

  2. MySQL事务锁等待超时 Lock wait timeout exceeded; try restarting transaction

    工作中处理定时任务分发消息时出现的问题,在查找并解决问题的时候,将相关的问题博客收集整理,在此记录下,以便之后再遇到相同的问题,方便查阅. 问题场景 问题出现的场景: 在消息队列处理消息时,同一事务内 ...

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

    本文为博主原创: 以下为在程序运行过程中报的错误, org.springframework.dao.CannotAcquireLockException: ### Error updating dat ...

  4. com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction 问题解决

    有两种设置方法 第一种在mysql的配置文件中加入,然后重启mysql innodb_lock_wait_timeout = 500 第二种直接执行如下命令 set global innodb_loc ...

  5. Mysql错误:ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

    昨晚添加完索引之后, 查询整表的时候抛出Lock wait timeout exceeded; try restarting transaction, 吓死小白的我, 为什么条件查询可以, 整表查不了 ...

  6. Mysql错误: ERROR 1205: Lock wait timeout exceeded解决办法(MySQL锁表、事物锁表的处理方法)

    Java执行一个SQL查询未提交,遇到1205错误. java.lang.Exception: ### Error updating database.  Cause: java.sql.SQLExc ...

  7. mysql异常Lock wait timeout exceeded; try restarting transaction

    mysql中使用update语句更新数据报错: Lock wait timeout exceeded; try restarting transaction. 这是由于你要更新的表的锁在其它线程手里. ...

  8. Lock wait timeout exceeded?代码该优化了

    背景 最近在排查问题时发现,偶尔会发生关于数据库锁超时的现象,会发生像如下的报错信息: Exception in thread "pool-3-thread-1" org.spri ...

  9. MySQL事务锁问题-Lock wait timeout exceeded

    转载:https://cloud.tencent.com/developer/article/1356959 问题现象:   接口响应时间超长,耗时几十秒才返回错误提示,后台日志中出现Lock wai ...

随机推荐

  1. 聊聊OOP中的设计原则以及访问者模式

    一  设计原则 (SOLID) 1.  S - 单一职责原则(Single Responsibllity Principle) 1.1  定义 一个类或者模块只负责完成一个职责(或功能), 认为&qu ...

  2. 在 GitHub 上玩转开源项目的 Code Review

    一.幕后故事 时光荏苒,岁月如梭-- (太文绉绉了,这不是我的风格) 今天我准备聊聊在 GitHub 上如何玩 Code Review. 突发奇想?心血来潮?不是. 咋回事呢?(对八卦不感兴趣的可以直 ...

  3. java标识符 identifier

    1,标识符 --> 类名 方法名  变量名 常量名 接口名   为程序员自己命名的内容 main也是标识符但是不能修改 2, 命名规则 只能以   数字 字母(中文) 下划线 美元符号      ...

  4. SAP BDC 用户输入日期转系统日期格式: CONVERT_DATE_TO_EXTERNAL

    BDC中,日期输入格式不正确:可调用FM  CONVERT_DATE_TO_EXTERNAL DATA:l_bdcfield LIKE bdcdata-fval."BDC field val ...

  5. ssm框架layui分页下标中文乱码,或者请选择中文乱码,提示乱码等

    开始我以为是layui的bug 后来发现不是 用过的方法: 1.修改layui的js文件  将其中的中文变为encdoe 代码 比如laypage.js下的中文 2.添加web.xml的过滤器 该代码 ...

  6. Mybatis-Plus介绍

    Mybatis-Plus介绍 Mybatis-Plus概念 Mybatis-Plus介绍 官网https://mybatis-plus/或https://mp.baomidou.com/ mybati ...

  7. MyBatis 映射文件

    Mybatis映射文件简介 1) MyBatis 的真正强大在于它的映射语句.由于它的异常强大,映射器的 XML 文件就显得相对简单.如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉 ...

  8. Identity Server 4客户端认证控制访问API

    项目源码: 链接:https://pan.baidu.com/s/1H3Y0ct8xgfVkgq4XsniqFA 提取码:nzl3 一.说明 我们将定义一个api和要访问它的客户端,客户端将在iden ...

  9. Etcd 使用场景:通过分布式锁思路实现自动选主

    分布式锁?选主? 分布式锁可以保证当有多台实例同时竞争一把锁时,只有一个人会成功,其他的都是失败.诸如共享资源修改.幂等.频控等场景都可以通过分布式锁来实现. 还有一种场景,也可以通过分布式锁来实现, ...

  10. 常用类-jdk8之前的日期和API

    一.System静态方法 点击查看代码 package com.Tang.StringDay01; import org.junit.Test; public class DateTimeTest { ...