死锁日志如下:
TRANSACTION 48AA4BB9, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 6 lock struct(s), heap size 1248, 4 row lock(s), undo log entries 2
MySQL thread id 1409173, OS thread handle 0x5659f940, query id 1084083936 10.246.138.197 bop_libra update
insert into deadlock_test
(deadlock_config_id, block_id, type, gmt_create, gmt_modified)
values
(31643, 92354, 1, now(), now());
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 17 page no 161 n bits 584 index `idx_block_id` of table `deadlock`.`test_deadlock` trx id 48AA4BB9 lock_mode X insert intention waiting
*** (2) TRANSACTION:
TRANSACTION 48AA4BBF, ACTIVE 0 sec inserting, thread declared inside InnoDB 500
mysql tables in use 1, locked 1
5 lock struct(s), heap size 1248, 3 row lock(s), undo log entries 2
MySQL thread id 1393832, OS thread handle 0x7699f940, query id 1084083946 10.246.138.197 bop_libra update
insert into deadlock_test
(deadlock_config_id, block_id, type, gmt_create, gmt_modified)
values
(31643, 92353, 1, now(), now());
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 17 page no 161 n bits 584 index `idx_block_id` of table `deadlock`.`test_deadlock` trx id 48AA4BBF lock_mode X
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 17 page no 161 n bits 584 index `idx_block_id` of table `deadlock`.`test_deadlock` trx id 48AA4BBF lock_mode X insert intention waiting
*** WE ROLL BACK TRANSACTION (2)
表上有聚集索引和二级索引,死锁发生在二级索引idx_block_id上。
首先,从死锁信息:
RECORD LOCKS space id 17 page no 161 n bits 584 index `idx_block_id` of table `deadlock`.`test_deadlock` trx id 48AA4BBF lock_mode X
可以确定,这个x锁不是由于INSERT产生的。
INSERT可能产生的锁包括检查dup key时的s锁,隐式锁转换为显式锁(not gap,要在二级索引上产生lock_mode为X的LOCK_ORDINARY类型的锁(包括记录及记录前面的gap),据我所知一般是根据二级索引扫描进行记录更新导致的。
从5 lock struct(s), heap size 1248, 3 row lock(s), undo log entries 2
有2个undo entires,而单纯的INSERT一条记录只有一个undo entry,因此可以推断除了INSERT,必然还有别的操作.)
基于以上,事务除了INSERT,可能还存在DELETE/UPDATE,并且这些操作是走的二级索引来查找更新记录。
一个简单但不完全相同的重现步骤:
DROP TABLE t1;
CREATE TABLE `t1` (
`a` int(11) NOT NULL AUTO_INCREMENT,
`b` int(11) DEFAULT NULL,
`c` int(11) DEFAULT NULL,
PRIMARY KEY (`a`),
KEY `b` (`b`)
) ENGINE=InnoDB ;
insert into t1(a, b,c) values(1,2,3),(5,4,6),(8, 7,9),(12,12,19),(15,15,11);
session1:
begin;
delete from t1 where b = 12;
//二级索引上lock_mode X、lock_mode X locks gap before rec以及主键上的lock_mode X locks rec but not gap
二级索引:heap_no=5, type_mode=3 (12上的LOCK_ORDINARY类型锁,包括记录和记录前的GAP)
聚集索引:heap_no=5,type_mode=1027
二级索引:heap_no=6,type_mode=547(15上的GAP锁)
session2:
begin;
delete from t1 where b = 7;
//二级索引上lock_mode X、lock_mode X locks gap before rec以及主键上的lock_mode X locks rec but not gap
二级索引:heap_no=4,type_mode=3 (7上的LOCK_ORDINARY类型锁,包括记录和记录前的GAP)
聚集索引:heap_no=4,type_mode=1027
二级索引:heap_no=5,type_mode=547 (记录12上的GAP锁)
session1:
insert into t1 values (NULL, 6,10);
//新插入记录聚集索引无冲突插入成功,二级索引等待插入意向锁(lock_mode X locks gap before rec insert intention waiting)
二级索引,heap_no=4, type_mode=2819 (请求记录7上面的插入意向锁LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION, 需要等待session2
session2:
insert into t1 values (NULL, 7,10);
二级索引:heap_no=5, type_mode=2819 (请求记录12上的插入意向锁LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION,需要等待session1)
互相等待,导致发生死锁
从打印的死锁信息来看,基本和线上发生的死锁现象是一致的。