I. Introduction
Deadlock, in fact, is a very interesting and very challenging technical problems, probably each part of the development and DBA students will meet in the course of their work. Stories About Deadlock I will continue to write a series of analysis, hoping to help friends want to know deadlock
Two case studies
2.1 Business scene
User input goods, the application will check in advance whether the same record, if you remove and then insert; if not directly inserted.
2.2 Environmental instructions
MySQL 5.7.22 transaction isolation level RC mode.
create table t(id int not null auto_increment primary key ,
a int not null default 0,
b int not null default 0,
c int not null default 0,
unique key uk_ab(a,b)) engine=innodb;
insert into t(a,b,c) values(1,1,1),(3,3,2),(6,6,3),(9,9,5);
2.3 Background
A knowledge point
INSERT operations when inserting or updating records, check the duplicate key or a duplicate key is marked for deletion (article case), for ordinary INSERT / UPDATE, will add attributes lock LOCK_S next-key lock. For this or similar REPLACE INTO SQL INSERT ... ON DUPLICATE X lock is applied. And for different index types are different:
Code Location row0ins.cc:2013
if (flags & BTR_NO_LOCKING_FLAG) {
/* Set no locks when applying log
in online table rebuild. */
} else if (allow_duplicates) {
/* If the SQL-query will update or replace
duplicate key we will take X-lock for
duplicates ( REPLACE, LOAD DATAFILE REPLACE,
INSERT ON DUPLICATE KEY UPDATE). */
err = row_ins_set_exclusive_rec_lock(
lock_type, block, rec, index, offsets, thr);
} else {
err = row_ins_set_shared_rec_lock(
lock_type, block, rec, index, offsets, thr);
}
Knowledge point two
When a record is inserted into a data page, the function calls will always be lock_rec_insert_check_and_lock lock check (except insertion of indexing data), the lock will check whether there is a next record current object insertion position, where the does not refer to a physically continuous recording, but in accordance with the next record in a logical order. If the lock object does not exist on the next record: If the record is to update the maximum transaction ID on the secondary index page on the secondary index for the ID of the current transaction; direct return success.
If there is a next record lock object, it is necessary to determine whether the lock object is locked GAP. If the GAP is locked, and determine intent and insert GAP lock conflict, it is necessary to wait for the current operation, plus lock type LOCKX | LOCKGAP | LOCKINSERTINTENTION, and enters a wait state. Code Location lock0lock.cc:5965
*inherit = TRUE;
/* If another transaction has an explicit lock request which locks
the gap, waiting or granted, on the successor, the insert has to wait.
An exception is the case where the lock by the another transaction
is a gap type lock which it placed to wait for its turn to insert. We
do not consider that kind of a lock conflicting with our insert. This
eliminates an unnecessary deadlock which resulted when 2 transactions
had to wait for their insert. Both had waiting gap type lock requests
on the successor, which produced an unnecessary deadlock. */
const ulint type_mode = LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION;
const lock_t* wait_for = lock_rec_other_has_conflicting(
type_mode, block, heap_no, trx);
if (wait_for != NULL) {
RecLock rec_lock(thr, index, block, heap_no, type_mode);
trx_mutex_enter(trx);
err = rec_lock.add_to_waitq(wait_for);
trx_mutex_exit(trx);
} else {
err = DB_SUCCESS;
}
I verified by the following test. Table 2.2 data structure and the test data structure.
A test case
sess1
mysql > delete from t where a=3 and b=3 ;
Query OK, 1 row affected (0.00 sec)
sess2
mysql >update t set c=6 where a=6 and b=6 and c=3;
sess1
mysql >insert into t(a,b,c) values(3,3,5); --产生锁等待
insert (3,3,5) application lock S is blocked sess2 delete hold the Lock X row lock,
show engine innodb status does not show the complete lock S is what locks. We continue to test.
Test Case II
T1 sess1
mysql > delete from t where a=3 and b=3 ;
mysql > insert into t(a,b,c) values(3,3,5);
T2 sess2
mysql > insert into t(a,b,c) values(3,2,6);
T3 sess3
mysql > insert into t(a,b,c) values(3,4,5);
Wherein sess2 sess3 waiting application lock_mode X locks gap before rec insert intention waiting, apparently held sess1 LOCK S Next key lock obstruction. And is (1,3), (3,6) of the two sections
Obviously a test case in sess2 hold record (6,6) of the lock X record lock but not gap, will block insert (3,3) application LOCK S Next key lock.
2.4 Test Case
2.5 Deadlock log
2019-04-27 23:26:16 0x7f26cc77b700
*** (1) TRANSACTION:
TRANSACTION 2489, ACTIVE 43 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 5 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 2
MySQL thread id 121125, OS thread handle 139804595451648, query id 526 localhost msandbox update
insert into t(a,b,c) values(3,3,3),(3,1,2)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 26 page no 4 n bits 80 index uk_ab of table `test`.`t` trx id 2489 lock mode S waiting
*** (2) TRANSACTION:
TRANSACTION 2490, ACTIVE 36 sec inserting
mysql tables in use 1, locked 1
6 lock struct(s), heap size 1136, 6 row lock(s), undo log entries 3
MySQL thread id 121123, OS thread handle 139804615882496, query id 528 localhost msandbox update
insert into t(a,b,c) values(6,6,6),(6,5,4)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 26 page no 4 n bits 80 index uk_ab of table `test`.`t` trx id 2490 lock_mode X locks rec but not gap
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 26 page no 4 n bits 80 index uk_ab of table `test`.`t` trx id 2490 lock_mode X locks gap before rec insert intention waiting
*** WE ROLL BACK TRANSACTION (1)
2.6 Analysis of deadlock log
T1 delete a = 3 and b = 3 uncommitted, holds the secondary index (3,3) line lock record is marked as stale.
T2 delete a = 6 and b = 6 uncommitted, holds the secondary index (6,6) line lock record is marked as stale.
T3 insert (3,3,3), the check is marked for deletion (3,3), applying plus LOCK_S next-key lock. But in the checking to the next record holders Lock X record lock. So wait.
Interval T4 insert (6,5,4) writing (3,6), the applicant lock_mode Locks GAP before X-REC INSERT Waiting at intention , but is waiting for the session T3 LOCK_S Next-Key Lock . Then waiting for each other, a deadlock occurs.
2.7 Solution
Essentially concurrent operation of adjacent record results in a deadlock. And the development of communication, business logic will make changes, if the number of items found records of entry and exist of the number of records updated to do the same, there is written directly. Reducing the possibility of directly adjacent recording operation.
Three summary
The above analysis is based on their ability to read the code out of halfway decent, it may not be entirely correct. If you have additional comments, please Paizhuan.
No micro-channel public attention and headlines today, excellent article continued update. . . . .