What, the code written by a colleague caused the database to deadlock

background

The new project is about to go online. When testing the function, I found that the page was stuck after clicking the button. At first I thought it was a network problem, but the page stuck and reproduced 100%. Check the background log and found that it was locked while executing the update statement.

image

Query via sql

select * from information_schema.innodb_trx;

We found that it was caused by updating the table sys_sn_rule, so let's sort out the code and see why LOCK_WAIT appears.

Cause investigation

By looking at the interface call, we locate a method, here I will simplify the method. The simplified code is as follows:

@Override
@Transactional(rollbackFor = Exception.class)
public Result funA() {
    //更新表table1;
    funB();
    ...
}

public void funB() {
   //更新表table1;
   ....
}

The problem lies in that funA calls funB, and the two methods operate on the same piece of data in table1 at the same time. There will be two transactions here. When the data is updated, both transactions will be waiting for each other to close the transaction, which will deadlock at that time. Let's illustrate with a picture:

image

Deadlock diagram

As shown in the figure above, when funA is executed, table 1 will be updated, transaction A will be opened before the table is updated, and this row of data will be locked when the table is updated (in order to protect data consistency). Next, call funB, open transaction B, and update table table1. Because the row of table1 has been locked, transaction B needs to wait for the lock to be released before it can continue. However, if transaction A wants to be closed, it needs to be closed after funA is executed. FunB is called in funA, and funB has to wait for table1 to release the lock before it can be executed. This leads to an endless loop.

For the knowledge of database locking, you can read this article:

The Road to Solving Deadlock-Learning Transaction and Isolation Level-aneasystone's blog

(https://www.aneasystone.com/archives/2017/10/solving-dead-locks-one.html)

Solution

We can use multi-threading to solve:

@Override
@Transactional(rollbackFor = Exception.class)
public Result funA() {
    //更新表table1;
    taskExecutor.execute(() -> {
        try {
            Thread.sleep(5 * 1000);
            funB();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
    ...
}

public void funB() {
   //更新表table1;
   ....
}

With multithreading, the two transactions are in two different threads, and there will be no loop waiting.

image

Deadlock Diagram-Multithreading

After the code was modified, the test passed smoothly.

Recommended in the past

Scan the QR code to get more exciting. Or search Lvshen_9 on WeChat , you can reply to get information in the background

  1. 回复"java" 获取java电子书;

  2. 回复"python"获取python电子书;

  3. 回复"算法"获取算法电子书;

  4. 回复"大数据"获取大数据电子书;

  5. 回复"spring"获取SpringBoot的学习视频。

  6. 回复"面试"获取一线大厂面试资料

  7. 回复"进阶之路"获取Java进阶之路的思维导图

  8. 回复"手册"获取阿里巴巴Java开发手册(嵩山终极版)

  9. 回复"总结"获取Java后端面试经验总结PDF版

  10. 回复"Redis"获取Redis命令手册,和Redis专项面试习题(PDF)

  11. 回复"并发导图"获取Java并发编程思维导图(xmind终极版)

Another: Click [ My Benefits ] to have more surprises.

Guess you like

Origin blog.csdn.net/wujialv/article/details/113392118