Let's start from transaction:
CREATE TABLE `tu`(`id` int(11), `a` int(11) DEFAULT NULL, `b` varchar(10) DEFAULT NULL, `c` varchar(10) DEFAULT NULL, PRIMARY KEY(`id`), UNIQUE KEY `u`(`a`,`b`)) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
|
insert into tu values(1,1,'a','a'),(2,9999,'xxxx','x'),(3,10000,'b','b'),(4,4,'c','c');
|
|
start transaction;
|
delete from tu where a = 9999 and b = 'xxxx';
|
delete from tu where a = 9999 and b = 'xxxx';
|
show engine innodb status;
|
Last line will output (if lock monitor is on) following :
---TRANSACTION 303 OS thread 140129523119872, ACTIVE 0 sec
|
5 lock struct(s), heap size 1248, 4 row lock(s), undo log entries 1
|
MySQL thread id 2, OS thread handle 0x7f727272b700, query id 27 localhost root
|
show engine innodb status
|
TABLE LOCK table `test`.`tu` trx id 303 lock mode IX
|
RECORD LOCKS space id 0 page no 309 n bits 72 index `u` of table `test`.`tu` trx id 303 lock_mode X locks rec but not gap
|
Record lock, heap no 3 PHYSICAL RECORD: n_fields 3; compact format; info bits 32
|
0: len 4; hex 8000270f; asc ' ;;
|
1: len 4; hex 78787878; asc xxxx;;
|
2: len 4; hex 80000002; asc ;;
|
|
RECORD LOCKS space id 0 page no 308 n bits 72 index `PRIMARY` of table `test`.`tu` trx id 303 lock_mode X locks rec but not gap
|
Record lock, heap no 3 PHYSICAL RECORD: n_fields 6; compact format; info bits 32
|
0: len 4; hex 80000002; asc ;;
|
1: len 6; hex 000000000303; asc ;;
|
2: len 7; hex 04000001370110; asc 7 ;;
|
3: len 4; hex 8000270f; asc ' ;;
|
4: len 4; hex 78787878; asc xxxx;;
|
5: len 1; hex 78; asc x;;
|
|
RECORD LOCKS space id 0 page no 309 n bits 72 index `u` of table `test`.`tu` trx id 303 lock_mode X
|
Record lock, heap no 3 PHYSICAL RECORD: n_fields 3; compact format; info bits 32
|
0: len 4; hex 8000270f; asc ' ;;
|
1: len 4; hex 78787878; asc xxxx;;
|
2: len 4; hex 80000002; asc ;;
|
|
RECORD LOCKS space id 0 page no 309 n bits 72 index `u` of table `test`.`tu` trx id 303 lock_mode X locks gap before rec
|
Record lock, heap no 4 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
|
0: len 4; hex 80002710; asc ' ;;
|
1: len 1; hex 62; asc b;;
|
2: len 4; hex 80000003; asc ;;
|
Lets investigate taken locks one by one:
- First lock is taken to the index record when we search index 'u' for matching row.
- Second lock is taken for clustered record matching the index record above.
- Third X-lock is taken to the index record that we have already above marded to be deleted
- Forth gap lock is taken for index record gap between xxxx and b on index 'u'
Now there will be no more lock records even if you add more delete-clauses to open transaction. Now first and second lock are trivially necessary and these are taken by the first delete. Rest of the locks are taken the second delete-clause.
Let's first assume that second delete-clause would be from different transaction.
Forth lock is needed for repeatable read to avoid concurrent transaction inserting a record to index 'u' between 9999,'xxxx' and 10000,'b'. Can you insert anything there, actually yes you can e.g. record 99999,'xxxxx' but that would not be result set of the delete clause. But this gap lock also protects from insert with record 9999,'xxxx'.
Finally, third lock is needed because we need to serialize after transaction marked the index-record to be deleted so we need to wait until it is committed.
Now, lets get back to original case where both delete-clauses are in the same transaction. Firstly, as secondary indexes do not contain TRX_ID system field we do not easily know which transaction has delete marked the secondary index record. Secondly, requested lock modes are either stronger (X-lock > X-lock rec not gap) or on different record (X-lock heap 3, gap lock heap 4).
Can we do better, absolutely we could add TRX_ID system field, note that we have already taken necessary index-record locks in the same transaction and not take additional locks. However, this is not feasible on GA-release. Additionally, we could investigate at second delete is there a possibility to insert a record that would cause result set to change, that would naturally decrease the performance of operations and decrease the concurrency as this investigation takes time and is done holding page latches. Again, not feasible on GA-release.
http://bugs.mysql.com/bug.php?id=83640