问题

Lock wait timeout exceeded; try restarting transaction 

详细异常

UPDATE `rbac_user` t SET t.`login_count` = 0, `pwd_expire_day` = 90, `last_login_time` = TIMESTAMP '2024-10-25 10:39:08.000', `status` = '0', `update_user` = 'rbac_user_superuser', `update_date` = TIMESTAMP '2024-10-25 10:39:08.625' WHERE t.id IN ('03xqnu2hykkzt')
2024-10-25 10:39:18.685 [pool-8-thread-1] ERROR c.s.snest.engine.db.relationdb.RelationDBAccessor -数据源:main,执行SQL失败:Lock wait timeout exceeded; try restarting transaction 




示例代码

/**
     * 创建用户服务
     *
     * @param user
	 * @throws InterruptedException 
     */
    @MethodService(description = "创建用户")
    public void createUser() throws InterruptedException {
     
        
        
        String sql="replace\r\n"
            + "into\r\n"
            + "    test_user (\r\n"
            + "id,\r\n"
            + "    name,\r\n"
            + "    age,\r\n"
            + "    `password`,\r\n"
            + "    create_user,\r\n"
            + "    create_date,\r\n"
            + "    update_user,\r\n"
            + "    update_date\r\n"
            + ")\r\n"
            + "select\r\n"
            + "    d.id,\r\n"
            + "    d.name,\r\n"
            + "    d.age,\r\n"
            + "    d.tenant_id,\r\n"
            + "    u1.name,\r\n"
            + "    d.create_date,\r\n"
            + "    u2.name,\r\n"
            + "    d.update_date\r\n"
            + "from\r\n"
            + "    edo_user d,\r\n"
            + "    rbac_user u1,\r\n"
            + "    rbac_user u2\r\n"
            + "where\r\n"
            + "    u1.id = d.create_user\r\n"
            + "    and u2.id = d.update_user";
        
        
    
        
        
        BussModelDataAccess bussModelDataAccess = (BussModelDataAccess) ModelDataAccessFactory.getDataAccess(ModelTypeEnum.Buss);
        RelationDBAccessor dataAccessor = bussModelDataAccess.getRelationDBAccessor();
        dataAccessor.execute(sql);
      
        
        
//        
//        TestUser user=new TestUser();
//        user.setId("10000");
//        user.setName(System.currentTimeMillis()+"");
//        user.setAge(32);
//        user.setEmail(System.currentTimeMillis()+"@qq.com");
//        user.setPassword("123456");
//        user.create();
        
        TestUser testUser = super.selectById("10000");
        if (testUser != null) {
            testUser.setEmail("cdchencw@120.com");
            testUser.setAge(5);
            testUser.update();
        }
        
        

        TestRole role = new TestRole();
        role.setRoleName("test");
        role.set("remark", "测试");
        role.create();
        
        Thread.sleep(11000);
        System.out.println("执行完毕: #0001");
    }
    


长事物导致更改rbac_user表的数据锁超时,事物没有结束所以拿不到锁。

经过排查到一段业务比较特殊,是REPLACE INTO,并且有可能导致长事物,并且会行锁rbac_user表

REPLACE INTO test_user (id, name, age, `password`, create_user, create_date, update_user, update_date) SELECT d.id, d.name, d.age, d.tenant_id, u1.name , d.create_date, u2.name, d.update_date FROM edo_user d, rbac_user u1, rbac_user u2 WHERE u1.id = d.create_user AND u2.id = d.update_user
待投批次数据统计定时任务出现异常:

解决方案

————-chat GPT ——-

是的,REPLACE INTO 语句在 MySQL 中确实可能导致死锁,尤其是在高并发环境下。以下是一些导致死锁的常见原因和解决方案:

死锁原因 唯一键冲突:REPLACE INTO 操作会尝试插入新记录,如果发现唯一键冲突,则会删除旧记录并插入新记录。这一过程中涉及到多个锁操作,可能会导致死锁 。 锁的类型和顺序:REPLACE INTO 操作需要获取多个锁,包括行锁、间隙锁和插入意向锁。在高并发环境下,不同事务获取锁的顺序不同,可能导致死锁 。 锁等待:当一个事务持有锁并等待另一个事务释放锁,而另一个事务也在等待第一个事务释放锁时,就会发生死锁 。 解决方案 使用 INSERT … ON DUPLICATE KEY UPDATE:这种方式在处理唯一键冲突时,只会更新现有记录,而不会删除再插入,减少了锁操作的复杂性 。

sql


INSERT INTO table_name (column1, column2)
VALUES (value1, value2)
ON DUPLICATE KEY UPDATE column2 = value2;