栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

深入MySQL死锁场景

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

深入MySQL死锁场景

总结死锁需满足以下条件:

  • 2个或者2个以上的并发事务操作
  • 并发事务之间存在锁冲突
  • 锁冲突关系成环形

GAP锁和Insert的隐式锁,最容易导致死锁,以下分析从这俩典型场景开始。

1. 表结构

建立以下表作为场景验证,id为主键,使用InnoDB,版本是5.7+,隔离级别RR。

CREATE TABLE `trigger` (
  `id` char(50) NOT NULL,
  `name` varchar(128) DEFAULT NULL,
  `cron` varchar(50) DEFAULT '0',
  `timeout` int(10) DEFAULT '0',
  `status` int(1) DEFAULT '0',
  `job_id` char(50) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `index_id` (`id`) USING BTREE,
  KEY `idx_name` (`name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8
2. 死锁场景一

原因分析:行记录不存在时,加记录X锁变成加GAP X锁,GAP X锁可共存,插入意向锁与GAP X冲突导致死锁。

t1时刻:select for update where id=3加锁过程:默认加NextKey锁,但id=3不存在,退化为GAP锁

t2时刻:T2事务同样获取到GAP锁,因为GAP锁是共存锁

t3时刻:INSERT加锁过程:因为其他事务有GAP锁,需要加插入意向锁,等待T2释放

t4时刻:因为其他事务有GAP锁,需要加插入意向锁,等待T1释放,死锁

t3时的锁信息如下(select * from information_schema.innodb_locks):

GAP X锁,锁的范围是(下一个存在的索引值,上一个存在的索引值) 注意是开区间,id=3在这个范围内,所以当加插入意向锁时会冲突。

锁等待信息如下:

 tips:因为做了多次复现操作,事务ID不一定完全一致,能说明问题即可。

show engine innodb status 查看死锁信息。

T1事务在等待插入意向锁,T2拥有一个GAP X锁,同样在等待插入意向锁,检测到死锁,选择了T2进行回滚。

3.死锁场景二

原因分析:插入数据唯一索引冲突时,先获取GAP S锁,GAP S锁可共存,但与记录X锁冲突,插入意向锁与GAP S锁也会冲突,最终导致死锁。

t1时刻:T1 INSERT实际上未加锁,因为无任何冲突

t2时刻:T2出现INSERT唯一索引冲突,会给T1增加一个记录X锁,自身获取GAP S锁时,等待

t3时刻:T3同上获取GAP S锁时阻塞

t4时刻:T1操作回滚,释放记录X锁,T2和T3得到GAP S锁,接着获取插入意向锁,但与其他事务GAP S锁冲突,死锁。

t3时的锁信息如下(select * from information_schema.innodb_locks):

锁等待信息如下 

 死锁信息如下

只看到T2和T3,因为T1已经回滚结束。T2和T3都在等待获取插入意向锁,死锁状态。

补充一点innodb status的信息说明:

死锁信息显示

  • 记录锁(LOCK_REC_NOT_GAP): lock_mode X locks rec but not gap
  • 间隙锁(LOCK_GAP): lock_mode X locks gap before rec
  • Next-key 锁(LOCK_ORNIDARY): lock_mode X
  • 插入意向锁(LOCK_INSERT_INTENTION): lock_mode X locks gap before rec insert intention

 有一个例外:如果在 supremum record 上加锁,locks gap before rec会省略掉,间隙锁会显示成 lock_mode X,插入意向锁会显示成 lock_mode X insert intention

 infimum和supremum是系统生成的纪录,分别为最小和最大纪录值,infimum的下一条是用户纪录中键值最小的纪录,supremum的上一条是用户纪录中键值最大的纪录,通过next_record字段来相连

参考:

MySQL 中的 INSERT 是怎么加锁的?

MySQL :: MySQL 8.0 Reference Manual :: 15.7.1 InnoDB Locking

MySQL :: MySQL 8.0 Reference Manual :: 15.7.3 Locks Set by Different SQL Statements in InnoDB

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/992324.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号