MySQL Insert deadlock

insert deadlock

insert lock step

The general process of insert statement locking is as follows:

1. Apply for "Intentional Insertion Lock" in the gap where the row is located. 2. Apply for the "exclusive lock" of the row to be inserted. 3. If the unique key conflict is triggered in the second step, then the transaction that fell into conflict should divide the locking process into two steps, first apply for the "shared lock" of the row, and then apply for the "exclusive lock"; if there is If multiple things fall into conflict, they must all apply for a "shared lock", and then wait for each other (deadlock) when applying for exclusive locks. At this time, MySQL will choose to sacrifice some of these transactions and let one of them complete.


Reproduce the insert deadlock

Reproduce the scene of insert deadlock step by step according to theory.

Session One Session Two Session Three
create table t(x int primary key);    
start transaction;    
insert into t(x) values(1024);    
  insert into t(x) values(1024); insert into t(x) values(1024);
rollback commit successful ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

show engine innodb status The information about deadlock in the output is as follows.

------------------------
LATEST DETECTED DEADLOCK
------------------------
2019-09-03 19:29:30 0x7f9ab00b4700
*** (1) TRANSACTION:
TRANSACTION 2759830, ACTIVE 9 sec inserting
mysql tables in use 1, locked 1 LOCK WAIT 4 lock struct(s), heap size 1136, 2 row lock(s) MySQL thread id 13, OS thread handle 140302402717440, query id 1221 127.0.0.1 root update insert into t(x) values(1024) *** (1) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 16 page no 4 n bits 72 index PRIMARY of table `tempdb`.`t` trx id 2759830 lock_mode X insert intention waiting Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0 0: len 8; hex 73757072656d756d; asc supremum;; *** (2) TRANSACTION: TRANSACTION 2759831, ACTIVE 6 sec inserting mysql tables in use 1, locked 1 4 lock struct(s), heap size 1136, 2 row lock(s) MySQL thread id 14, OS thread handle 140302355220224, query id 1222 127.0.0.1 root update insert into t(x) values(1024) *** (2) HOLDS THE LOCK(S): RECORD LOCKS space id 16 page no 4 n bits 72 index PRIMARY of table `tempdb`.`t` trx id 2759831 lock mode S Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0 0: len 8; hex 73757072656d756d; asc supremum;; *** (2) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 16 page no 4 n bits 72 index PRIMARY of table `tempdb`.`t` trx id 2759831 lock_mode X insert intention waiting Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0 0: len 8; hex 73757072656d756d; asc supremum;; *** WE ROLL BACK TRANSACTION (2) 

Why does MySQL do this

The premise is that if two transactions insert the same record, one of them must fail. A deadlock must also have a failure, so why not incorporate concurrent insertion into the deadlock processing flow? When I think about it, I need to do it. I need to remove the process of upstream lock, first put on the "S shared lock" and then on the "X exclusive lock". Since "S" can be shared, both can complete this step. X "so the two transactions wait for each other (deadlock).


solution

If you really encounter the above scenario in business, MySQL is deadlocked. You don't want to let it deadlock, so what should you do? Didn't MySQL take apart the locking steps? Then we put it together.

select ... for update Directly on the line exclusive lock, through which we can combine the two separate steps, on duplicate key update when conflicts are encountered, they will be updated directly, and no error will be reported; combining these two artifact codes can be changed to the following form.

Session One Session Two Session Three
create table t(x int primary key);    
start transaction;    
select x from t for update; insert into t(x) values(1024) on duplicate key update x = 1024;    
  select x from t for update; insert into t(x) values(1024) on duplicate key update x = 1024; select x from t for update; insert into t(x) values(1024) on duplicate key update x = 1024;
rollback commit successful commit successful

side effect

Because it  select for update is an exclusive lock, there will be some problems in concurrency, it is recommended read-committedto use together.


Easter eggs

According to the analysis, we can know that the problem occurs on the row lock. Adjusting the isolation level  READ-COMMITTED will only affect the  gap lock. I thought that there will be no more intentional locks (a special gap) at this isolation level.

mysql> show variables like '%iso%';
+-----------------------+----------------+ | Variable_name | Value | +-----------------------+----------------+ | transaction_isolation | READ-COMMITTED | +-----------------------+----------------+ 1 row in set (0.00 sec) 

Do the same.

Session One Session Two Session Three
create table t(x int primary key);    
start transaction;    
insert into t(x) values(1024);    
  insert into t(x) values(1024); insert into t(x) values(1024);
rollback commit successful ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

------------------------
LATEST DETECTED DEADLOCK
------------------------
2020-04-11 13:57:15 0x7ff1bccf1700
*** (1) TRANSACTION:
TRANSACTION 6946, ACTIVE 12 sec inserting
mysql tables in use 1, locked 1 LOCK WAIT 4 lock struct(s), heap size 1136, 2 row lock(s) MySQL thread id 9, OS thread handle 140676621076224, query id 237 127.0.0.1 root update insert into t(x) values(1024) *** (1) HOLDS THE LOCK(S): RECORD LOCKS space id 5 page no 4 n bits 72 index PRIMARY of table `tempdb`.`t` trx id 6946 lock mode S Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0 0: len 8; hex 73757072656d756d; asc supremum;; *** (1) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 5 page no 4 n bits 72 index PRIMARY of table `tempdb`.`t` trx id 6946 lock_mode X insert intention waiting Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0 0: len 8; hex 73757072656d756d; asc supremum;; *** (2) TRANSACTION: TRANSACTION 6947, ACTIVE 9 sec inserting mysql tables in use 1, locked 1 LOCK WAIT 4 lock struct(s), heap size 1136, 2 row lock(s) MySQL thread id 13, OS thread handle 140676151592704, query id 258 127.0.0.1 root update insert into t(x) values(1024) *** (2) HOLDS THE LOCK(S): RECORD LOCKS space id 5 page no 4 n bits 72 index PRIMARY of table `tempdb`.`t` trx id 6947 lock mode S Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0 0: len 8; hex 73757072656d756d; asc supremum;; *** (2) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 5 page no 4 n bits 72 index PRIMARY of table `tempdb`.`t` trx id 6947 lock_mode X insert intention waiting Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0 0: len 8; hex 73757072656d756d; asc supremum;; *** WE ROLL BACK TRANSACTION (2) 

READ-COMMITTED There is no gap lock under the isolation level  , but the official document says that it  insert intention is a kind of gap, so it  insert intention should not exist.

Guess you like

Origin www.cnblogs.com/JiangLe/p/12680228.html