Mysql 死锁案例7-并发更新索引列导致的死锁

死锁复现

mysql 5.7.12 ,事务隔离级别RR

create table  x
(id int not null auto_increment,
 c int not null default  0,
 d int not null default 0,
 primary key(id),
 key idxcd(c,d));
insert into x(c,d) values(1,0),(3,0),(5,0),(7,0),(10,0),(12,0),(14,0),(16,0);
事务1事务2
T1BEGIN;
UPDATE X SET d=1 WHERE c=6
T2BEGIN;
UPDATE X SET d=1 WHERE c IN (5,10)(阻塞)
T3UPDATE X SET d=1 WHERE c=7 (死锁)

SHOW ENGINE INNODB STATUS 死锁日志:


LATEST DETECTED DEADLOCK
------------------------
2024-03-14 10:41:36 0x1d94
*** (1) TRANSACTION:
TRANSACTION 489120, ACTIVE 5 sec updating or deleting
mysql tables in use 1, locked 1
LOCK WAIT 5 lock struct(s), heap size 1136, 7 row lock(s), undo log entries 1
MySQL thread id 5, OS thread handle 1528, query id 2630 localhost ::1 root updating
UPDATE X SET d=1 WHERE c IN (5,10)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1089 page no 4 n bits 80 index idxcd of table `test`.`x` trx id 489120 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 10 PHYSICAL RECORD: n_fields 3; compact format; info bits 32
 0: len 4; hex 80000007; asc     ;;
 1: len 4; hex 80000000; asc     ;;
 2: len 4; hex 80000004; asc     ;;

*** (2) TRANSACTION:
TRANSACTION 489119, ACTIVE 8 sec updating or deleting
mysql tables in use 1, locked 1
5 lock struct(s), heap size 1136, 5 row lock(s), undo log entries 1
MySQL thread id 4, OS thread handle 7572, query id 2635 localhost ::1 root updating
update x set d=1 where c=7
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 1089 page no 4 n bits 80 index idxcd of table `test`.`x` trx id 489119 lock_mode X locks gap before rec
Record lock, heap no 6 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;
 1: len 4; hex 80000000; asc     ;;
 2: len 4; hex 80000005; asc     ;;

Record lock, heap no 10 PHYSICAL RECORD: n_fields 3; compact format; info bits 32
 0: len 4; hex 80000007; asc     ;;
 1: len 4; hex 80000000; asc     ;;
 2: len 4; hex 80000004; asc     ;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1089 page no 4 n bits 80 index idxcd of table `test`.`x` trx id 489119 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 6 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;
 1: len 4; hex 80000000; asc     ;;
 2: len 4; hex 80000005; asc     ;;

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

死锁分析

两个前提知识点

  • RR事务隔离级别下,对于通过索引(唯一或者非唯一)更新或者删除不存在的记录,会申请加上gap锁。
  • 当update 更新索引字段时,相当于删除之后重新插入新的记录,需要重新组织索引节点。

死锁过程:

  1. T1时刻 事务2 更新c=6的值,但是c=6 不存在,申请加上(5,0)---(7,0)的X gap锁,日志提示:持有idxcd of table `test`.`x` trx id 489119 lock_mode X locks gap before rec
  2. T2时刻 事务1 更新2行记录c=5,c=10,并且修改d=1,事务1 会申请(5,0),(10,0) 记录之间的Next-key锁,由于事务1 需要插入新的记录(5,1)事务2 持有的X GAP 锁范围内,根据锁的兼容性矩阵,GAP,Next-Key会阻塞插入意向锁INSERT_INTENTION,故日志提示:index idxcd of table `test`.`x` trx id 489120 lock_mode X locks gap before rec insert intention waiting
  3. T3时刻 事务2 更新c=7对应d=1,同样相当于insert (7,1) 在(5,0),(10,0)之间。根据兼容矩阵GAP,Next-Key会阻塞插入意向锁INSERT_INTENTION 故日志提示: index idxcd of table `test`.`x` trx id 489119 lock_mode X locks gap before rec insert intention waiting
  4. 至此,事务2 持有gap锁阻塞事务1 插入(5,1),事务1持有Next-key 阻塞事务2 插入(7,1),循环等待出现死锁。

解决方法

其实解决方法比较简单 把组合索引 idxcd(c,d) 中的d去掉,改为idxc(c),避免GAP/Next-key 阻塞插入意向锁INSERT_INTENTION 即可,开发同学18:10分左右修改索引之后,业务不再出现死锁。(注意如果单列索引,更新的字段还是索引字段还是会死锁,这跟是否是单列索引和联合索引名没关系,只能更新字段是否存在索引有关)

小结

死锁场景:

  1. 事务1更新一个不存在的记录,锁住间隙
  2. 事务2更新一个存在的记录的索引列,相当于delete后再insert,事务2插入意向锁与事务1的间隙锁冲突阻塞
  3. 事务1再更新一个存在的记录的索引列,相当于delete后再insert,事务1意向插入锁与事务2的临键锁冲突

本文的死锁算是蛮有意思的一个案例:并发多个update更新二级索引列,相当于索引节点重新组织,更新等于删除加插入,在死锁日志出现了插入意向锁。第一次猜想的时候还以为有 多个insert 操作,但是实际上只有更新动作。

注意本文案例和Mysql 死锁案例3-间隙锁与意向插入锁冲突 种update+insert死锁案例的区别,虽然本质都是意向插入锁和间隙锁冲突导致的死锁,但是本文没有显式的insert语句,全是update语句。

原文:MySQL 并发更新冗余索引字段导致的死锁

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值