1

@Transactional 导致报错 Lock wait timeout exceeded; try restarting transaction

前言

最近线上项目报

Lock wait timeout exceeded; try restarting transaction

排查

  1. 百度Lock wait timeout exceeded; try restarting transaction

    大概意思,就是mysql当中有死锁。需要排查

  2. 查看当前进程 show full processlist;
  3. 查看mysql锁表 select * from information_schema.innodb_trx;

    此处结果需要在意的是trx_state,trx_mysql_thread_id两个字段。

  4. 以上结果,没有trx_state = LOCK_WAIT的结果,就证明mysql内没有死锁。
  5. 迷茫了,不知道该咋解决了。
  6. 监控mysql吧,不停的刷show full processlist;
  7. 无意中发现一条sql查询语句,不停的再执行。去系统内查看此sql语句。
  8. 发现系统内一个service方法上面,使用了@Transactional
  9. 那么问题来了,使用了@Transactional 为什么会报 Lock wait timeout exceeded; try restarting transaction
  10. 猜想是否有一个任务,再执行此方法,其他的任务也来执行此方法。

测试猜想

  1. 写了两个方法,里面同时有select,update

    @Override
        @Transactional
        public Person getPerson()
        {
            System.out.println("getPerson1访问程序启动:===========》" + System.currentTimeMillis());
    
            Person person = getById(1);
            System.out.println(person.getName());
    
            person.setName("测试"+System.currentTimeMillis());
    
            updateById(person);
    
            try {
                Thread.sleep(100000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            System.out.println("getPerson1访问程序结束:===========》" + System.currentTimeMillis());
            return person;
        }
    
    @Override
        @Transactional
        public Person getPerson2()
        {
            System.out.println("getPerson2访问程序启动:===========》" + System.currentTimeMillis());
    
            Person person = getById(2);
            System.out.println(person.getName());
    
            person.setName("测试"+System.currentTimeMillis());
    
            updateById(person);
    
            try {
                Thread.sleep(100000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            System.out.println("访问程序结束:===========》" + System.currentTimeMillis());
            return person;
        }
  2. 写两个单元测试,分别同时请求

        @Test
        void demo2() {
            personService.getPerson();
        }
    
        @Test
        void demo3() {
            personService.getPerson2();
        }
  3. 结果
  • getById() 都是1的时候,复现了

image.png

image.png

  • getById()不同的时候,没有复现

![1642411180281]image.png

![1642411186155]image.png

结论

  1. @Transactional 是行级锁
  2. mysql 等待事务锁 innodb_lock_wait_timeout 默认50s
  3. 使用事务的情况下,尽量简化service代码。

氷落
7 声望1 粉丝

一个年过30,苦苦挣扎的程序员......