两次死锁的分析
最近业务上连续出现了两次死锁逻辑,两次都是特别简单的SQL语句,分析后才发现自己对InnoDB加锁了解得太浅了。
表结构
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(64) NOT NULL,
`age` int(11) DEFAULT NULL,
`deleted` int(22) DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
主键id
和唯一键name
死锁场景一
- SQL语句
select * from user where name='tenmao' for update;
insert into user(`name`) values('tenmao');
- 死锁日志
------------------------
LATEST DETECTED DEADLOCK
------------------------
2020-09-16 20:23:21 0x7f4b8b596700
*** (1) TRANSACTION:
TRANSACTION 425593, ACTIVE 21 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 65439, OS thread handle 139962440095488, query id 2992052 localhost maibao update
insert into user(`name`) values('tenmao')
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 370 page no 4 n bits 72 index uk_name of table `tenmao`.`user` trx id 425593 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) TRANSACTION:
TRANSACTION 425594, ACTIVE 12 sec inserting
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 65440, OS thread handle 139962437166848, query id 2992053 localhost maibao update
insert into user(`name`) values('tenmao')
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 370 page no 4 n bits 72 index uk_name of table `tenmao`.`user` trx id 425594 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 370 page no 4 n bits 72 index uk_name of table `tenmao`.`user` trx id 425594 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** WE ROLL BACK TRANSACTION (2)
- 死锁分析
T1 |
T2 |
|
---|---|---|
select * from user where name='tenmao' for update 记录不存在所以获取gap锁,模式是X |
||
select * from user where name='tenmao' for update 记录不存在所以获取gap锁,模式是X(因为gap锁之间不冲突,所以可以获取) |
||
insert into user(`name`) values('tenmao') 插入需要获取插入意向锁。因为与T2的gap锁冲突,需要等待 | ||
insert into user(`name`) values('tenmao') 插入需要获取插入意向锁。。因为与T1的gap锁冲突,需要等待。死锁! |
死锁场景二
- SQL语句
insert into user(`name`) values('tenmao');
- 死锁分析
T1 |
T2 |
---|---|
insert into user(`name`) values('tenmao') 第一阶段,需要判断duplicate key,所以获取S锁,类型是gap |
|
insert into user(`name`) values('tenmao') 第一阶段,需要判断duplicate key,所以获取S锁,类型是gap |
|
第二阶段,S锁升级为X锁。等待T2释放S锁 |
|
第二阶段,S锁升级为X锁。等待T2释放S锁(死锁) |
以上过程,因为S锁升级为X锁的时间间隔很短,所以不是很好复现,一般在高并发的时候出现。不过可以用3个事务来复现:
T1 |
T2 |
T3 |
---|---|---|
insert into user(`name`) values('tenmao'); 先获取S锁判断duplicate key,插入前升级为X锁 |
||
insert into user(`name`) values('tenmao'); 第一阶段,需要判断duplicate key,所以获取S锁,类型是gap,与T1的X锁冲突,等待 |
||
insert into user(`name`) values('tenmao'); 第一阶段,需要判断duplicate key,所以获取S锁,类型是gap,与T1的X锁冲突,等待 |
||
rollback |
获取到S锁,类型是gap |
获取到S锁,类型是gap |
第二阶段,S锁升级为X锁。等待T3释放S锁(死锁) |
第二阶段,S锁升级为X锁。等待T2释放S锁(死锁) |
参考
- 解决死锁之路(终结篇) - 再见死锁
- The Infimum and Supremum Records
- https://github.com/aneasystone/mysql-deadlocks/blob/master/1.md
- 事件处理需小心
- Mysql读写分离方案-MySQL Proxy环境部署记录
- Mysql读写分离方案-Amoeba环境部署记录
- linux系统终端命令提示符设置(PS1)记录
- 从MapX到MapXtreme2004[10]-根据zoom值修改显示范围
- Linq to Sql中Single写法不当可能引起的数据库查询性能低下
- 获得定长字符串
- vue2.0知识点汇总
- ie6,ie7,ff 的css兼容hack写法
- 使用子查询时应当注意的
- LegacyText的复制的Bug
- 提高WCF服务并发能力的简单处理办法
- 如何给已经有数据的DataTable动态增加一列并赋值
- Gitlab上采用rpm方式快速安装的操作记录
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Oracle ASM 环境增加控制文件
- 利用注解进行 Spring 开发
- 40张图揭秘,「键入网址发生了什么」
- Spring 代理模式知多少
- 一条SQL引发的“血案”:与SQL优化相关的4个案例
- Mycat分库分表全解析 Part 3 Mycat的安装
- Oracle非图形化界面创建数据库
- Mycat分库分表全解析 Part 5 Mycat 分片规则介绍
- PHP 命名空间与类自动加载实现
- mysqldump命令详解 Part 3-备份单表
- mysqldump命令详解 Part 7- -single-transaction 参数的使用
- 基于 Symfony 组件封装 HTTP 请求响应类
- 通过 PHP 原生代码实现 HTTP 路由器
- MySQL组复制(MGR)全解析 Part 10 MGR新增节点
- 基于 gorilla/sessions 在 Go 语言中管理 Session