问题
From the docs:
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.
Going with the example in the docs,
Suppose that an InnoDB table t1 has the following structure:
CREATE TABLE t1 (i INT, PRIMARY KEY (i)) ENGINE = InnoDB;
Now suppose that three sessions perform the following operations in order:
Session 1:
START TRANSACTION;
INSERT INTO t1 VALUES(1);
Session 2:
START TRANSACTION;
INSERT INTO t1 VALUES(1);
Session 3:
START TRANSACTION;
INSERT INTO t1 VALUES(1);
Session 1:
ROLLBACK;
The first operation by session 1 acquires an exclusive lock for the row. The operations by sessions 2 and 3 both result in a duplicate-key error and they both request a shared lock for the row. When session 1 rolls back, it releases its exclusive lock on the row and the queued shared lock requests for sessions 2 and 3 are granted. At this point, sessions 2 and 3 deadlock: Neither can acquire an exclusive lock for the row because of the shared lock held by the other.
I have some questions :
1) The insert query takes an exclusive lock on the row it is inserting. So, suppose T1 is inserting on row 1, it will lock row 1. Now when T2 comes to write, will INNODB evaluate the query before executing it and find out that it is going to write the same PK (row with i = 1) and make T2 wait? Or will it start execution of T2 and find that it gives duplicate key error or PK violation.
2) Why are T2 and T3 taking shared locks? How do shared locks come into picture during insert?
回答1:
1) The insert query takes an exclusive lock on the row it is inserting. So, suppose T1 is inserting on row 1, it will lock row 1. Now when T2 comes to write, will INNODB evaluate the query before executing it and find out that it is going to write the same PK (row with i = 1) and make T2 wait? Or will it start execution of T2 and find that it gives duplicate key error or PK violation.
I think you are simplifying the terminology/process. After the query is parsed and before it is executed, it needs to acquire necessary locks. It is at this point that it is determined that:
- session 1 gets the exclusive lock, because it is inserting and there are no other locks
- session 2 and 3 get queued for the shared lock because the exclusive lock is already held by session 1, and session 2 and 3 are in a duplicate key error
2) Why are T2 and T3 taking shared locks? How do shared locks come into picture during insert?
Per above, sessions 2 and 3 get queued for shared locks because they are in duplicate key error. However, when session 1 deletes the key and releases the exclusive lock, now both session 2 and 3 get granted shared locks. At this point both try to acquire an exclusive lock to complete the insert. Neither one can, though, because the other is already holding the shared lock. So exclusive lock is not granted to either and they deadlock.
回答2:
For the sessions 2 and 3: INNODB evaluate the query before executing and determine if the row has locked (row with i = 1), the transactions will wait for unlock the implied row.
When you execute
SHOW ENGINE INNODB STATUS
after insert in first session and after run the inserts in sessions 2 and 3:------------ TRANSACTIONS ------------ Trx id counter 2079155 Purge done for trx's n:o < 2079150 undo n:o < 0 state: running but idle History list length 594 LIST OF TRANSACTIONS FOR EACH SESSION: ---TRANSACTION 2079154, ACTIVE 21 sec inserting mysql tables in use 1, locked 1 LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s) MySQL thread id 540, OS thread handle 0x7ff989386700, query id 1683 localhost root update INSERT INTO t1 VALUES(1) ------- TRX HAS BEEN WAITING 21 SEC FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 4190 page no 3 n bits 72 index `PRIMARY` of table `temp`.`t1` trx id 2079154 lock mode S locks rec but not gap waiting Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0 0: len 4; hex 80000001; asc ;; 1: len 6; hex 0000001fb9af; asc ;; 2: len 7; hex 9c000001d30110; asc ;; ------------------ ---TRANSACTION 2079153, ACTIVE 43 sec inserting mysql tables in use 1, locked 1 LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s) MySQL thread id 541, OS thread handle 0x7ff989355700, query id 1680 localhost root update INSERT INTO t1 VALUES(1) ------- TRX HAS BEEN WAITING 43 SEC FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 4190 page no 3 n bits 72 index `PRIMARY` of table `temp`.`t1` trx id 2079153 lock mode S locks rec but not gap waiting Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0 0: len 4; hex 80000001; asc ;; 1: len 6; hex 0000001fb9af; asc ;; 2: len 7; hex 9c000001d30110; asc ;;
After unlock the row (rollback from session 1) the sessions 2 will get the error:
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
, the session 3:Query OK, 1 row affected
The sessions 2 and 3 doesn't have a shared lock, they are in queue to get one, because the lock of first session are exclusive, needs to wait. In this case the session 3 will get one and insert the record.
Shared lock: A kind of lock that allows other transactions to read the locked object, and to also acquire other shared locks on it, but not to write to it. The opposite of exclusive lock.
Exclusive lock: A kind of lock that prevents any other transaction from locking the same row. Depending on the transaction isolation level, this kind of lock might block other transactions from writing to the same row, or might also block other transactions from reading the same row. The default InnoDB isolation level, REPEATABLE READ, enables higher concurrency by allowing transactions to read rows that have exclusive locks, a technique known as consistent read.
回答3:
Question 2:
2) Why are T2 and T3 taking shared locks? How do shared locks come into picture during insert?
It requires a lock on the existing entry so that subsequent attempts to insert a duplicate record fail consistently。
来源:https://stackoverflow.com/questions/37972925/mysql-locking-in-duplicate-key-error