Talk about the deadlock pit of INSERT ON DUPLICATE KEY UPDATE

1. Background

There have been some activities recently, so I will do some suppression on the system with a small amount of paths. It is good if it is not suppressed. There is a strange problem with this suppression. There is a deadlock problem in an old code. , the log is as follows:

After seeing the log, I embarked on the road of deadlock investigation. Of course, if you are not very familiar with locks, you can read my two articles first to take a look at the basics of database locks: why developers must understand database locks: and remember a mysql deadlock troubleshooting of the artifact

2. Problem analysis

The database code is as follows:

CREATE TABLE `order_extrainfo` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `orderId` int(11) NOT NULL,
  `extraInfo` text NOT NULL,
  `appProductId` int(11) NOT NULL DEFAULT '0',
  `hostAppProductId` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `orderId` (`orderId`)
) ENGINE=InnoDB AUTO_INCREMENT=17835265 DEFAULT CHARSET=utf8mb4;

The SQL statement in question is as follows:

INSERT INTO `order_extrainfo` (orderId, " +
                            "extraInfo, appProductId, hostAppProductId) VALUES (?,?, ?, ?) ON " +
                            "DUPLICATE KEY UPDATE extraInfo = ?, appProductId = ?, " +
                            "hostAppProductId = ?

I have never encountered a deadlock in insert before, so I thought it was caused by on dpolicate key update. In order to find the deadlock scene at that time, enter: SHOW ENGINE INNODB STATUS; view the latest deadlock log:

Transaction 1 and transaction 2 are obviously waiting for the release of the gap lock. They should be holding gap locks with each other, and they are both waiting for each other to cause it.

The general deadlock log is caused by two transactions, so it will give a certain degree of confusion. In fact, most deadlocks are caused by more than two transactions. This time is no exception. This is actually mysql A bug, https://bugs.mysql.com/bug.php?id=52020, you can take a look if you are interested.

For the description in the bug, start to reproduce this scene locally:

timeline session1 session2 session3
1 begin; insert into xx
2 INSERT INTO order_extrainfo (orderId, extraInfo, appProductId, hostAppProductId) VALUES (158360183,'', 0, 0) ON DUPLICATE KEY UPDATE extraInfo = ''; begin;
3 1 row in affected; INSERT INTO order_extrainfo (orderId, extraInfo, appProductId, hostAppProductId) VALUES (158360184,'', 0, 0) ON DUPLICATE KEY UPDATE extraInfo = ''; begin;
4 INSERT INTO order_extrainfo (orderId, extraInfo, appProductId, hostAppProductId) VALUES (158360184,'', 0, 0) ON DUPLICATE KEY UPDATE extraInfo = '';
5 commit;
6 1 row in affected; Deadlock found when trying to get lock; try restarting transaction

Note: session1,2,3 each orderId is incremented in turn

Session3 deadlocked.

The execution order of session1,2,3 is easy to occur when we have high concurrency, so there will be a large number of deadlock errors.

2.1 Lock Analysis

Here, let's analyze in detail what locks are added. We know that X locks and insertion intention locks will be added during the insert operation. Here we will see what locks are added to insert into on duplicate key.

This is tested on my local computer, first open:

set GLOBAL innodb_status_output_locks=ON;

set GLOBAL innodb_status_output=ON;

MySQL's lock statistics, if this online is not recommended to open, the log will record more.

First execute the first sql:

INSERT INTO order_extrainfo (orderId, extraInfo, appProductId, hostAppProductId) VALUES (158360183,'', 0, 0) ON DUPLICATE KEY UPDATE extraInfo = '';

Enter the show engine innodb status command to view

The locking situation is shown in the figure above. What we want to explain here is that insert intention is an implicit lock here. The lock added here is actually x + GAP (gap lock from negative infinity to positive infinity) + insert intention three locks

Here we are executing the second sql,

INSERT INTO order_extrainfo (orderId, extraInfo, appProductId, hostAppProductId) VALUES (158360184,'', 0, 0) ON DUPLICATE KEY UPDATE extraInfo = '';

It is found that the insertion intent lock is being blocked by the gap lock.

Similarly, if we execute the third sql, the insertion intention lock will also be blocked by the gap lock of the first transaction. If the gap lock of the first transaction is committed, they will first acquire the gap lock (here, judging from the lock information, There is no gap lock when it is blocked), and then the insertion intent lock is acquired, which causes session2 and session3 to form a circular link, which eventually leads to a deadlock.

2.2 Why is there a gap lock

Gap lock is a means to solve phantom reading under RR isolation level. It usually appears in delete. Why does it appear here? This bug can be seen in https://bugs.mysql.com/bug.php?id=50413 :

"Concurrent "INSERT ...ON DUPLICATE KEY UPDATE" statements run on a table with multiple unique indexes would sometimes cause events to be written to the binary log incorrectly" ”

When we use INSERT ... ON DUPLICATE KEY UPDATE concurrently, if we have multiple unique indexes, it may cause binlog errors, that is, it will lead to inconsistent master-slave replication, and some specific tests can be viewed in the link

3. How to solve

What should I do if I encounter this problem? We have the following methods to solve this problem:

  • Using mysql5.6 version, you can see that this was introduced in 5.7, and this situation will not occur in 5.6
  • With RC level, there will be no gap lock under RC isolation level -- don't use insert on duplicate key update, use normal insert. We ended up using this method, because ON DUPLICATE KEY UPDATE is really unnecessary in the code
  • Only the primary key is established in the database table, and no other unique indexes are established.
  • Insert first, then catch exception, then update
  • Use insert ignore, and then judge whether update rows is 1, and then decide whether to update.

If you think this article is helpful to you, your attention and forwarding are the greatest support for me, O(∩_∩)O:

{{o.name}}
{{m.name}}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324052751&siteId=291194637