I have 1,000 vouchers with 2 different types. I want to give them to some special users on our website. So each user will get 1 vouchers for each type. To do that, I created a table t_voucher_pool :
CREATE TABLE `t_voucher_pool` (
`iAutoID` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`iCrowdID` INT(10) UNSIGNED DEFAULT NULL,
`sTypeCode` VARCHAR(50) NOT NULL,
`sCode` VARCHAR(255) NOT NULL,
`sPassword` VARCHAR(255) NOT NULL,
`iBindStatus` TINYINT(1) UNSIGNED NOT NULL DEFAULT '1',
`iVoucherID` INT(10) UNSIGNED DEFAULT NULL,
`iBindTime` INT(10) UNSIGNED DEFAULT NULL,
`iStatus` TINYINT(1) UNSIGNED NOT NULL DEFAULT '1',
`iCreateTime` INT(10) UNSIGNED DEFAULT NULL,
`iUpdateTime` INT(10) UNSIGNED DEFAULT NULL,
PRIMARY KEY (`iAutoID`),
KEY `idx_crowdid_typecode` (`iCrowdID`,`sTypeCode`,`iBindStatus`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
Here is the sql which will be executed in my logic:
1.START TRANSACTION;
2.SELECT * FROM t_voucher_pool WHERE `iStatus`=1 AND `iBindStatus`=1 AND `iCrowdID`=7 AND `sTypeCode`='LUCKYDRAW' LIMIT 1 FOR UPDATE; //get one available voucher for LUCKYDRAW
3.SELECT * FROM t_voucher_pool WHERE `iStatus`=1 AND `iBindStatus`=1 AND `iCrowdID`=7 AND `sTypeCode`='JD' LIMIT 1 FOR UPDATE; //get one available voucher for JD
4.UPDATE t_voucher_pool SET iBindStatus=2 WHERE iAutoID=401; //update bindStatus for LUCKYDRAW voucher
5.UPDATE t_voucher_pool SET iBindStatus=2 WHERE iAutoID=10401; //update bindStatus for JD
6.COMMIT;
This worked fine when the concurrency level is 1. But when the concurrency level got raised, I found some requests will fail and they were caused by a deadlock. Here the deadlock will be detected if session 2 is pending on step 1 while session 1 tries to execute step 5.
Here is the sequence to reproduce the deadlock:
session1>>START TRANSACTION;
session1>>SELECT * FROM t_voucher_pool WHERE `iStatus`=1 AND `iBindStatus`=1 AND `iCrowdID`=7 AND `sTypeCode`='LUCKYDRAW' LIMIT 1 FOR UPDATE;
session2>>START TRANSACTION;
session2>>SELECT * FROM t_voucher_pool WHERE `iStatus`=1 AND `iBindStatus`=1 AND `iCrowdID`=7 AND `sTypeCode`='LUCKYDRAW' LIMIT 1 FOR UPDATE;(pending)
session1>>SELECT * FROM t_voucher_pool WHERE `iStatus`=1 AND `iBindStatus`=1 AND `iCrowdID`=7 AND `sTypeCode`='JD' LIMIT 1 FOR UPDATE;
session1>>UPDATE t_voucher_pool SET iBindStatus=2 WHERE iAutoID=401;
session1>>UPDATE t_voucher_pool SET iBindStatus=2 WHERE iAutoID=10401;
session2>>ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
I guess an update to the field bindStatus cause this deadlock. But since I've already got X lock for this record on step 3, why this still happen?
Any detailed explantion on this will be very appreciate. Thanks.
UPDATE on 2015/3/6
Sorry guys, it seems the example I posted before can not reproduce the problem. I just updated the example and here is the information from the innodb status:
2015-03-06 09:10:53 7f975a02b700
*** (1) TRANSACTION:
TRANSACTION 264943793, ACTIVE 44 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 6608579, OS thread handle 0x7f98f81b5700, query id 284931572 192.168.10.16 devadmin Sending data
SELECT * FROM t_voucher_pool WHERE `iStatus`=1 AND `iBindStatus`=1 AND `iCrowdID`=7 AND `sTypeCode`='LUCKYDRAW' LIMIT 1 FOR UPDATE
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 10453 page no 156 n bits 792 index `idx_crowdid_typecode` of table `crowd_db`.`t_voucher_pool` trx id 264943793 lock_mode X waiting
Record lock, heap no 320 PHYSICAL RECORD: n_fields 4; compact format; info bits 32
0: len 4; hex 00000007; asc ;;
1: len 9; hex 4c55434b5944524157; asc LUCKYDRAW;;
2: len 1; hex 01; asc ;;
3: len 4; hex 00000191; asc ;;
*** (2) TRANSACTION:
TRANSACTION 264941874, ACTIVE 374 sec updating or deleting, thread declared inside InnoDB 4999
mysql tables in use 1, locked 1
9 lock struct(s), heap size 2936, 7 row lock(s), undo log entries 2
MySQL thread id 6608580, OS thread handle 0x7f975a02b700, query id 284931884 192.168.10.16 devadmin updating
update t_voucher_pool set iBindStatus=2 where iAutoID=10401
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 10453 page no 156 n bits 792 index `idx_crowdid_typecode` of table `crowd_db`.`t_voucher_pool` trx id 264941874 lock_mode X
Record lock, heap no 320 PHYSICAL RECORD: n_fields 4; compact format; info bits 32
0: len 4; hex 00000007; asc ;;
1: len 9; hex 4c55434b5944524157; asc LUCKYDRAW;;
2: len 1; hex 01; asc ;;
3: len 4; hex 00000191; asc ;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 10453 page no 156 n bits 792 index `idx_crowdid_typecode` of table `crowd_db`.`t_voucher_pool` trx id 264941874 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 320 PHYSICAL RECORD: n_fields 4; compact format; info bits 32
0: len 4; hex 00000007; asc ;;
1: len 9; hex 4c55434b5944524157; asc LUCKYDRAW;;
2: len 1; hex 01; asc ;;
3: len 4; hex 00000191; asc ;;
*** WE ROLL BACK TRANSACTION (1)