Mysql 死锁案例4-delete 相邻记录导致死锁

本文详细描述了一个MySQL死锁案例,涉及间隙锁与意向插入锁在事务中的冲突,以及如何通过死锁日志分析事务间的锁竞争。作者探讨了事务隔离级别对死锁的影响,并提出了如何复现特定情况下的死锁。
摘要由CSDN通过智能技术生成

死锁复现 

CREATE TABLE `t` (
  `id` int(11) NOT NULL,
  `c` int(11) DEFAULT NULL,
  `d` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `c` (`c`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
/*Data for the table `t` */
 
insert  into `t`(`id`,`c`,`d`) values (0,0,0),(5,5,5),(10,10,10),(15,15,15)
事务1事务2
T1

BEGIN;

DELETE FROM  t WHERE  id=4;

T2

BEGIN;

DELETE FROM  t WHERE  id=4;

T3INSERT INTO t VALUES(3, 3,3);(阻塞)
T4INSERT INTO t VALUES(3, 3,3);(死锁)

死锁分析
注意where条件的c是普通索引

  1. T1事务1加主键索引间隙锁(0,5)写锁成功,
  2. T2事务2加主键索引间隙锁(0,5)写锁成功
  3. T3事务1申请意向插入锁(0,5)与事务2间隙锁(0,5)冲突阻塞
  4. T3事务2申请意向插入锁(0,5)与事务1间隙锁(0,5)冲突阻塞,循环等待死锁

这里其实也是间隙锁和意向插入锁冲突死锁,delete与update加锁逻辑差不多。参考:

Mysql 死锁案例2-间隙锁与意向插入锁冲突

查看锁信息 SHOW ENGINE INNODB STATUS 

------------------------
LATEST DETECTED DEADLOCK
------------------------
2024-03-13 16:47:14 0x1ed8
*** (1) TRANSACTION:
TRANSACTION 488371, ACTIVE 63 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 6, OS thread handle 2368, query id 2676 localhost ::1 root update
INSERT INTO t VALUES(3, 3,3)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 638 page no 3 n bits 80 index PRIMARY of table `test`.`t` trx id 488371 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 80000005; asc     ;;
 1: len 6; hex 000000076d2c; asc     m,;;
 2: len 7; hex 69000001ba0495; asc i      ;;
 3: len 4; hex 80000005; asc     ;;
 4: len 4; hex 80000000; asc     ;;

*** (2) TRANSACTION:
TRANSACTION 488372, ACTIVE 51 sec inserting
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 20, OS thread handle 7896, query id 2681 localhost ::1 root update
INSERT INTO t VALUES(3, 3,3)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 638 page no 3 n bits 80 index PRIMARY of table `test`.`t` trx id 488372 lock_mode X locks gap before rec
Record lock, heap no 3 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 80000005; asc     ;;
 1: len 6; hex 000000076d2c; asc     m,;;
 2: len 7; hex 69000001ba0495; asc i      ;;
 3: len 4; hex 80000005; asc     ;;
 4: len 4; hex 80000000; asc     ;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 638 page no 3 n bits 80 index PRIMARY of table `test`.`t` trx id 488372 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 80000005; asc     ;;
 1: len 6; hex 000000076d2c; asc     m,;;
 2: len 7; hex 69000001ba0495; asc i      ;;
 3: len 4; hex 80000005; asc     ;;
 4: len 4; hex 80000000; asc     ;;

*** WE ROLL BACK TRANSACTION (2)
------------
TRANSACTIONS

where语句等值匹配的delete语句加锁逻辑分主键索引和唯一索引和普通索引,规则参考:

MySQL行锁加锁规则之等值查询

普通索引删除的加锁案例:

DELETE FROM  t WHERE  c=5;
---TRANSACTION 488378, ACTIVE 6 sec
4 lock struct(s), heap size 1136, 3 row lock(s), undo log entries 1
MySQL thread id 6, OS thread handle 2368, query id 2744 localhost ::1 root
TABLE LOCK table `test`.`t` trx id 488378 lock mode IX
RECORD LOCKS space id 638 page no 4 n bits 80 index c of table `test`.`t` trx id 488378 lock_mode X
Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 32
 0: len 4; hex 80000005; asc     ;;
 1: len 4; hex 80000005; asc     ;;

RECORD LOCKS space id 638 page no 3 n bits 80 index PRIMARY of table `test`.`t` trx id 488378 lock_mode X locks rec but not gap
Record lock, heap no 3 PHYSICAL RECORD: n_fields 5; compact format; info bits 32
 0: len 4; hex 80000005; asc     ;;
 1: len 6; hex 0000000773ba; asc     s ;;
 2: len 7; hex 25000001392ce4; asc %   9, ;;
 3: len 4; hex 80000005; asc     ;;
 4: len 4; hex 80000000; asc     ;;

RECORD LOCKS space id 638 page no 4 n bits 80 index c of table `test`.`t` trx id 488378 lock_mode X locks gap before rec
Record lock, heap no 4 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;
 1: len 4; hex 8000000a; asc     ;;

加了3把锁,索引c间隙锁(5,10),主键索引记录锁id=5,索引c临键锁(0,5]

有兴趣的可以看下这篇文章:MySQL delete 相邻记录导致死锁

 死锁日志

------------------------
LATEST DETECTED DEADLOCK
------------------------
2017-09-09 22:34:13 7f78eab82700
*** (1) TRANSACTION:
TRANSACTION 462308399, ACTIVE 33 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 3525577, OS thread handle 0x7f896cc4b700, query id 780039657 localhost root updating
delete from ty where a=5
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 219 page no 4 n bits 72 index `idxa` of table `test`.`ty` trx id 462308399 lock_mode X waiting
*** (2) TRANSACTION:
TRANSACTION 462308398, ACTIVE 61 sec inserting, thread declared inside InnoDB 5000
mysql tables in use 1, locked 1
5 lock struct(s), heap size 1184, 4 row lock(s), undo log entries 2
MySQL thread id 3525490, OS thread handle 0x7f78eab82700, query id 780039714 localhost root update
insert into ty(a,b) values(2,10)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 219 page no 4 n bits 72 index `idxa` of table `test`.`ty` trx id 462308398 lock_mode X
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 219 page no 4 n bits 72 index `idxa` of table `test`.`ty` trx id 462308398 lock_mode X locks gap before rec insert intention waiting
*** WE ROLL BACK TRANSACTION (1)

分析死锁日志 

首先要理解的是 对同一个字段申请加锁是需要排队.  其次表ty中索引idxa为非唯一普通索引,我们根据事务执行的时间顺序来解释,这样比较好理解(MySQL 5.6 事务隔离级别为RR)。

  •  a. 根据死锁日志显示 事务2 也即sess1执行的事务,根据 HOLDS THE LOCK(S)显示    sess1 先执行 delete from ty where a=5 ,该事务持有索引a=5 的行锁lock_mode X ,因为是RR隔离级别,所以sess1 还持有两个gap锁[1,2]-[2,5], [2,5]-[3,6] 。
  • b. 事务1的日志也即sess2执行的事务,申请对 a=5 加锁,一个rec lock 和两个gap锁,因为sess1中delete还没释放,故sess2的事务1等待sess1的事务2释放a=5的锁资源。
  • c. 然后根据WAITING FOR THIS LOCK TO BE GRANTED,提示事务2 insert语句正在等待 lock_mode X locks gap before rec insert intention waiting, 因为insert语句 [4,2] 介于gap锁[1,2]-[2,5]之间,所以有了提示 "lock_mode X locks gap",insert语句必须等待前面 sess2中delete 获取锁并且释放锁。于是,sess2(delete) 等待sess1(delete) ,sess1(insert)等待sess2(delete),循环等待,造成死锁。 问题 如果sess1 执行 insert into ty(a,b) values(5,10); sess2会遇到死锁吗?

因为作者没贴具体的表结构和表数据,所以没看懂这个死锁, 也没复现出来(Mysql 5.7.12 事务隔离级别为RR)。

按对锁的理解,事务1事务2执行的第一个delete语句都能成功,只有一种可能,就是数据库没a=5这条记录,间隙锁与间隙锁不冲突。按这个设定执行,那么事务2的第二个delete语句就不会阻塞,所以也就不存在死锁。怎么复现这个死锁????

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值