continue about deadlock

Continuing the analysis of deadlocks: The last analysis was mostly superficial and conceptual. This article will further analyze the deadlock problem that actually occurs in the project.
Before analyzing, let’s clarify a few issues:
MVCC:
In InnoDB of MySQL, the implementation is based on the multi-version concurrency control protocol (MVCC): read without lock, read and write without conflict.
There are two types of read operations in MVCC:
Snapshot read: The read is to record the current version (possibly a historical version) without locking. A simple select statement is a snapshot read and does not lock: select * from test where ?.
Current reading: The latest version is read, why murmur, because in this case, the read records will be locked, and other records will not have the opportunity to change this record. Special reads: select * from test where ? lock in share mode; select * from table where ? for update; insert/update/delete/; all belong to the current read.

Four isolation levels defined by MySQL/InnoDB:
Read Uncommited: Uncommitted records can be read. This level will not be used.
Read Committed(RC): Locks (record locks) the records read, and there is a phantom read phenomenon.
Repeatable Read (RR): Lock the record read (record lock), and at the same time lock the range of the read record, new records within the range cannot be inserted (GAP lock), and there is no phantom read.
Serializable: Degenerates from MVCC concurrency control to lock-based concurrency control. All read operations are current read, read plus read lock (S lock), and write plus write lock (X lock).

Intention lock: Before a transaction requests S lock and X lock, it needs to obtain the corresponding IS and IX locks.
Intent Shared Lock (IS): A transaction wants to acquire a shared lock on certain rows in a table.
Intentional exclusive lock (IX): The transaction wants to acquire an exclusive lock on certain rows in a table.
Intent locks do not block any requests other than full table queries. Transaction A and transaction B can obtain IS and IX of the same rows of data at the same time.

Row Locks:
Record Locks: Lock only one row of the index record. Locking on a single index record will always lock the index, not the record itself.

Gap Locks: Interval locks, which lock an index interval: specifically, locking in the gap between index records, or before or after an index record, excluding the index itself.

Next-Key Locks: record lock+gap lock, left open and right closed interval. Innodb uses this lock by default to lock records. However, when the index of the query contains a unique attribute, Next-Key Lock will be optimized and downgraded to Record Lock, that is, only the index itself, not the range, will be locked.

Insert Intention Locks: There is an insertion intention lock in Gap Locks, which is generated during insert. When multiple transactions write different data to the same index gap at the same time, there is no need to wait for other transactions to complete, and no lock waiting occurs.
Suppose there is a record index containing key values ​​4 and 7, and different transactions insert 5 and 6 respectively. Each transaction will generate an insert intent lock between 4 and 7 to acquire an exclusive lock on the inserted row, but Will not be locked with each other, because the data rows do not conflict.

AUTO-INC Locks: A special table-level lock that occurs during transactional insert operations involving AUTO_INCREMENT columns.

Compatibility matrix for row locks:



Table Note: Horizontal is a lock already held, Vertical is a lock being requested.

One: delete statement locking mechanism:
delete action without index + RR:
delete from t1 where id = 10; there is no index on id, only full table scan can be performed. All records are X-locked, and the gap between each record is also GAP-locked.

Two: insert statement locking mechanism:
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.

A simple insert will add an exclusive lock on the index record corresponding to the inserted row. This is a record lock and there is no gap, so it will not block other sessions from inserting records in the gap.
Before insert, there will also be a kind of lock called insertion intention gap lock, which is the intention gap lock. The function of this intention gap lock is to indicate that when multiple transactions insert the same gap concurrently, as long as the inserted record is not a gap The same position in the gap can be completed without waiting for other sessions, so that the insert operation does not need to add a real gap lock. Concurrent transactions can add intention gap locks to the same gap.
Assuming a duplicate-key error occurs, a shared lock will be placed on the duplicate index record. This shared lock will cause deadlock in the case of concurrency. For example, if there are two concurrent inserts, both need to add a shared lock to the same record. At this time, this record has been added an exclusive lock by another transaction. When the transaction is committed or rolled back. Two concurrent insert operations will deadlock (Ta waits for Tb to release the shared lock before going down, and Tb waits for Ta to release the shared lock before going down).
When inserting, the LOCK_X exclusive lock | LOCK_GAP gap lock | LOCK_INSERT_INTENTION is used to insert the intention lock to check the insertion gap. In this mode, it conflicts with the lock mode of the LOCK_S shared lock | LOCK_GAP gap lock, and is in conflict with the LOCK_X exclusive lock | LOCK_GAP gap lock. Lock mode conflict. But for the same gap GAP, two lock modes LOCK_X exclusive lock | LOCK_GAP gap lock | LOCK_INSERT_INTENTION insert intent lock are compatible.

Insert deadlock scenario analysis: The following scenarios are for reference http://yeshaoting.cn/article/database/mysql%20insert%E9%94%81%E6%9C%BA%E5%88%B6/
1. The deadlock caused by duplicate key error
   mainly occurs in two or more The transaction simultaneously inserts records with the same unique key.



If T1 is commit, T2 and T3 will report a unique key conflict: ERROR 1062 (23000): Duplicate entry '6' for key 'PRIMARY'
If T1 is rollback, then T3 will report Deadlock.
The result of this offline operation is consistent with the above scenario.

Deadlock cause

1, transaction T1 successfully inserted records and obtained an exclusive record lock (LOCK_X | LOCK_REC_NOT_GAP) on index id=6.
2. Then transactions T2 and T3 also start inserting records, requesting an exclusive insertion intent lock (LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION); but due to repeated unique key conflicts, the respective requested exclusive insertion intent locks (LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION) are converted into Shared record lock (LOCK_S | LOCK_REC_NOT_GAP).
3. T1 rolls back and releases the exclusive record lock (LOCK_X | LOCK_REC_NOT_GAP) on index id=6, and both T2 and T3 request the exclusive record lock (LOCK_X | LOCK_REC_NOT_GAP) on index id=6.
Since the X lock and the S lock are mutually exclusive, both T2 and T3 wait for the other party to release the S lock.
Deadlock occurs!

2. Deadlock
table :
CREATE TABLE `t` (
  `a` int(11) NOT NULL,
  `b` int(11) DEFAULT NULL,
  PRIMARY KEY (`a`),
  KEY `idx_b` (`b`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

data:
mysql> select * from t;
+----+------+
| a  | b    |
+----+------+
|  1 |    2 |
|  2 |    3 |
|  3 |    4 |
| 11 |   22 |
+----+------+



Deadlock cause
1. Transaction T1 executes the query statement and adds an exclusive Next-key lock (LOCK_X | LOCK_ORDINARY) on index b=6, which locks the idx_b index range (4, 22).
2. Transaction T2 executes the query statement and adds an exclusive Next-key lock (LOCK_X | LOCK_ORDINARY) on index b=8, which locks the idx_b index range (4, 22). Since the requested GAP is compatible with the already held GAP, transaction T2 can also be successfully locked in the idx_b index range (4, 22).
3. When transaction T1 executes an insert statement, an exclusive Insert Intention lock will be added first. Since the requested Insert Intention lock is incompatible with the existing GAP lock, transaction T1 waits for T2 to release the GAP lock.
4. Transaction T2 executes the insert statement and also waits for T1 to release the GAP lock.
A deadlock occurs.

Back to the deadlock situation in the project:
Please see the following error log (MySQL):
BACKGROUND THREAD
-----------------
srv_master_thread loops: 990 srv_active, 0 srv_shutdown, 8035 srv_idle
srv_master_thread log flush and writes: 9025
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 3350
OS WAIT ARRAY INFO: signal count 3305
RW-shared spins 0, rounds 4136, OS waits 2044
RW-excl spins 0, rounds 561, OS waits 14
RW-sx spins 56, rounds 303, OS waits 3
Spin rounds per wait: 4136.00 RW-shared, 561.00 RW-excl, 5.41 RW-sx
------------------------
LATEST DETECTED DEADLOCK
------------------------
2017-03-21 15:10:36 0x2d08
*** (1) TRANSACTION:
TRANSACTION 3342674, ACTIVE 0 sec inserting, thread declared inside InnoDB 5000
mysql tables in use 1, locked 1
LOCK WAIT 6 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 1
MySQL thread id 142, OS thread handle 2932, query id 61023 localhost 127.0.0.1 root update
insert into XXXX
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 4580 page no 3 n bits 72 index PRIMARY of table `XXX`.`XXX` trx id 3342674 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 3342675, ACTIVE 0 sec inserting, thread declared inside InnoDB 5000
mysql tables in use 1, locked 1
6 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 1
MySQL thread id 147, OS thread handle 11528, query id 61024 localhost 127.0.0.1 root update
insert into XXX
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 4580 page no 3 n bits 72 index PRIMARY of table `XXX`.`XXX` trx id 3342675 lock_mode X
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 4580 page no 3 n bits 72 index PRIMARY of table `XXX`.`XXX` trx id 3342675 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)
------------
TRANSACTIONS
------------
Trx id counter 3343825
Purge done for trx's n:o < 3343825 undo n:o < 0 state: running but idle
History list length 2
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 283307779134672, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 283307779139904, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 283307779139032, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 283307779137288, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 283307779133800, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 283307779132928, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 283307779135544, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 283307779132056, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 283307779131184, not started
0 lock struct(s), heap size 1136, 0 row lock(s)

Because it is involved in the project, I just hide the fields of the table and insert. Everything else is Log as it is.
The above involves two transactions, which are doing delete and insert operations respectively. The deletion of the delete statement is to use a field without an index to delete and delete a piece of data; the new data of the insert statement is the data deleted in the delete statement.
According to the experience introduced above, X lock and GAP lock will be added when delete is deleted, then when two transactions are in delete, because GAP locks are compatible, all two transactions will acquire locks with the same gap. Then when insert is executed, an insert intent lock will be applied for the same gap lock, because the insert intent lock and the GAP lock are incompatible. All transactions A are waiting for transaction B to release the gap lock, and transaction B is waiting for transaction A to release the gap lock, so the above deadlock will occur.

Guess you like

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