zoukankan      html  css  js  c++  java
  • Spring单事务多线程操作引来的问题(Lock wait timeout exceeded; try restarting transaction)

    1、场景描述

    今天开发中遇到一个场景,在一个事务中的操作逻辑是:需要先删除A表的某个记录,然后多线程往A表里插入多条数据。

    begin

    delete from A where age=2;

    以下多线程操作>>>

    insert into A valus(5);

    ......

    insert into A valus(5);

    <<<

    commit;

    但是这样操作会会导致delete from A where age=2;执行完后,后续的插入获取锁超时,从而导致整个事务的失败回滚。

    出现:Error updating database.  Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction

    2、问题分析(结合本人真实业务代码分析)

    核心代码

     

    问题分析

    • applicationDirMapper.deleteByExample(exampleQuery);

      这段代码执行后会在当前事务发起索引表效应。因为当前delete后面跟的条件字段applicationId他不是一个主键或者普通索引,所以会导致锁表。

    • applicationServerDTO.getFolders().parallelStream().forEach(dirId -> {
        ApplicationDir applicationDir = new ApplicationDir();
        applicationDir.setApplicationId(applicationServer.getId());
        applicationDir.setDirId(dirId);
        applicationDirMapper.insertSelective(applicationDir);
      });

      这段代码分析parallelStream是一个多线程操作。Spring这时候会给这里的每个线程分配一个共享的SqlSession,为什么确定说分配了一个新的SqlSession呢?我们以日志来证明。

      这个代码实际可以看成里面有5个过程。

      第1步:80行是个查询操作

      第2步:82行是个更新操作

      第3步:93行是个查询操作

      第4步:94行是个删除操作

      第5步:95行是个多线程插入操作

    从1-5这5个步骤,如果再Spring单事务中没有出现多线程的话1-5的操作的SqlSession他是相同的,不会一个操作创建一个SqlSession的,这也是为什么Spring保证事务的条件,因为全局的一个SqlSession我们才可以全局的去控制当前事务的原子性,要不全部提交成功要不全部失败回滚。好了那我们怎么看出当前我们这个场景他就脱离了当前事务的SqlSession呢?看一下日志,本地开发真实输出的日志。先上个局部图,又有日志过多,后面会以文本的形式给大家把日志核心完整的展示出来。

     

     从上面的日志可以看出来过了第4步之后Spring就重新又Creating a new SqlSession了,导致后面的多线程里操作数据库的SqlSession和当前事务中的SqlSession脱离了,多线程脱轨到另外一个独立的事务中去了。

     当前事务的SqlSession

    org.apache.ibatis.session.defaults.DefaultSqlSession@7aac05e2

    多线程事务的SqlSession

    org.apache.ibatis.session.defaults.DefaultSqlSession@2337080f

     由此可见由于当前事务第4步删除操作导致锁表,但是多线程的插入操作有获取不到当前事务的锁而当前事务被阻塞无法释放锁,导致多线程获取锁超时永远等待中所以超时出现Lock wait timeout exceeded; try restarting transaction。

    3、解决方案

    a. 如果当前操作没有书屋要求可以去掉@Transactional,这样事务就可以自动提交不会导致锁的获取超时。

    b.可以将多线程里面的代码块抽离出来通过异步消息去处理。

    c.大家有什么好的想法可以帮忙指导一下。

    4、总结问题

     开发中需要了解mysqld的锁的类型,锁的原理,锁的算法,事务的隔离级别,事务的传播属性这样才能更安全的介入开发中。

     

  • 相关阅读:
    【Express系列】第3篇——接入mysql
    【Express系列】第2篇——主程序的改造
    【Express系列】第1篇——项目创建
    AngularJS内置指令
    node服务端搭建学习笔记
    生成ssh key
    webstorm的常用操作
    VSCode 常用插件
    php集成包
    composer安装特别慢的解决方案
  • 原文地址:https://www.cnblogs.com/dszazhy/p/14703460.html
Copyright © 2011-2022 走看看