MySQL 唯一索引,并发插入导致死锁

716 阅读4分钟

一日志

------------------------
LATEST DETECTED DEADLOCK
------------------------
2020-07-27 16:28:53 0x7fc914aee700
*** (1) TRANSACTION:
TRANSACTION 484991260, ACTIVE 0 sec setting auto-inc lock
mysql tables in use 2, locked 2
LOCK WAIT 3 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 319333, OS thread handle 140501656090368, query id 185921901 192.168.1.2 beta executing
INSERT INTO p_session(transaction_id,status) SELECT '18','ON' FROM dual WHERE NOT EXISTS (SELECT transaction_id FROM p_session WHERE transaction_id = '18')
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
TABLE LOCK table `mmm`.`p_session` trx id 484991260 lock mode AUTO-INC waiting


*** (2) TRANSACTION:
TRANSACTION 484991259, ACTIVE 0 sec inserting
mysql tables in use 2, locked 2
5 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 319335, OS thread handle 140501612160768, query id 185921900 192.168.1.2 beta executing
INSERT INTO p_session(transaction_id,status) SELECT '18','ON' FROM dual WHERE NOT EXISTS (SELECT transaction_id FROM p_session WHERE transaction_id = '18')
*** (2) HOLDS THE LOCK(S):
TABLE LOCK table `msc`.`p_session` trx id 484991259 lock mode AUTO-INC
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1019 page no 8 n bits 600 index uniq_transaction_id of table `mmm`.`p_session` trx id 484991259 lock_mode X locks gap before rec insert intention waiting
*** WE ROLL BACK TRANSACTION (1)
------------
TRANSACTIONS
------------------------------------
LATEST DETECTED DEADLOCK
------------------------
2020-07-27 16:28:53 0x7fc914aee700
*** (1) TRANSACTION:
TRANSACTION 484991260, ACTIVE 0 sec setting auto-inc lock
mysql tables in use 2, locked 2
LOCK WAIT 3 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 319333, OS thread handle 140501656090368, query id 185921901 192.168.1.2 beta executing
INSERT INTO p_session(transaction_id,status) SELECT '18','ON' FROM dual WHERE NOT EXISTS (SELECT transaction_id FROM pay_session WHERE transaction_id = '18')
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
TABLE LOCK table `mmm`.`p_session` trx id 484991260 lock mode AUTO-INC waiting


*** (2) TRANSACTION:
TRANSACTION 484991259, ACTIVE 0 sec inserting
mysql tables in use 2, locked 2
5 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 319335, OS thread handle 140501612160768, query id 185921900 192.168.1.2 beta executing
INSERT INTO p_session(transaction_id,status) SELECT '18','ON' FROM dual WHERE NOT EXISTS (SELECT transaction_id FROM p_session WHERE transaction_id = '18')
*** (2) HOLDS THE LOCK(S):
TABLE LOCK table `mmm`.`p_session` trx id 484991259 lock mode AUTO-INC
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1019 page no 8 n bits 600 index uniq_transaction_id of table `mmm`.`p_session` trx id 484991259 lock_mode X locks gap before rec insert intention waiting
*** WE ROLL BACK TRANSACTION (1)
------------
TRANSACTIONS
------------

这个问题:网上找了半天,没找到相同的死锁日志。

环境:MySQL 5.7 。隔离级别RR,自增锁级别默认=1.

表p_session有唯一索引uniq_transaction_id,id是自增的列。

虽然问题已经接决了,尝试下写下我的理解。

 

tran1 ,trans 2 在insert select获取到了 next key锁。

官网例子是3个并发插入的情况

INSERT sets an exclusive lock on the inserted row. This lock is an index-record lock, not a next-key lock (that is, there is no gap lock) and does not prevent other sessions from inserting into the gap before the inserted row.

Prior to inserting the row, a type of gap lock called an insert intention gap lock is set. This lock signals the intent to insert in such a way that multiple transactions inserting into the same index gap need not wait for each other if they are not inserting at the same position within the gap. Suppose that there are index records with values of 4 and 7. Separate transactions that attempt to insert values of 5 and 6 each lock the gap between 4 and 7 with insert intention locks prior to obtaining the exclusive lock on the inserted row, but do not block each other because the rows are nonconflicting.

If a duplicate-key error occurs, a shared lock on the duplicate index record is set. This use of a shared lock can result in deadlock should there be multiple sessions trying to insert the same row if another session already has an exclusive lock. This can occur if another session deletes the row. Suppose that an InnoDB table t1 has the following structure:

官网说的意思是:If a duplicate-key error occurs, a shared lock on the duplicate index record is set.。就是拿到了共享的S 锁。

再看官网关于自增锁的解释: 

innodb_autoinc_lock_mode = 1 (“consecutive” lock mode)

This is the default lock mode. In this mode, “bulk inserts” use the special AUTO-INC table-level lock and hold it until the end of the statement. This applies to all INSERT ... SELECT, REPLACE ... SELECT, and LOAD DATA statements. Only one statement holding the AUTO-INC lock can execute at a time. If the source table of the bulk insert operation is different from the target table, the AUTO-INC lock on the target table is taken after a shared lock is taken on the first row selected from the source table. If the source and target of the bulk insert operation are the same table, the AUTO-INC lock is taken after shared locks are taken on all selected rows.

默认的模式=1,If the source and target of the bulk insert operation are the same table, the AUTO-INC lock is taken after shared locks are taken on all selected rows.  操作同一张表,所以自增锁是在共享S锁之后拿到。

*******************

回到日志。

trans1 拿到共享锁之后,等待trans 2的 自增锁。

trans2 已经拿到了自增锁,要申请插入意向锁。(插入意向锁又等待trans1 的共享锁)产生了死锁。

 

**************

纯粹猜测,请大佬们明示。