Innodb执行insert造成死锁的问题

背景

起因是线上报了一个错误信息,Deadlock found when trying to get lock; try restarting transaction,这是Mysql检测到死锁后,自动回滚了事务引发的异常。spring框架里打印了异常堆栈,所以很快就定位到哪一行代码出了问题。

业务的伪代码如下:

val result = query("select * from game where user_id = xx and game_id = xx")
if (result == null) {excute("Insert Into game_player(game_id,user_id) Values(?,?)")
}-- table schema
create table game (id bigint,game_id bigint,user_id bigint,primary key(id),constraint unique_game_id_user_id unique (game_id, user_id)
)

看了代码逻辑,只有insert的时候会加一个行锁,死锁要有两个锁被多个线程账户等待,才会发生。就算有并发的情况,同时插入两条一样的记录,第二个事务也是处于等待锁的状态,等待锁超时后就报获取锁超时,也不会发生死锁。

查看死锁信息

然后通过show engine innodb status命令看一下死锁的相关信息。

------------------------
LATEST DETECTED DEADLOCK
------------------------
2023-12-29 05:43:26 22399693092608
*** (1) TRANSACTION:
TRANSACTION 3701497, ACTIVE 29 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 4 lock struct(s), heap size 1128, 2 row lock(s), undo log entries 1
MySQL thread id 43921, OS thread handle 22399139387136, query id 225853288 122.225.228.122 admin update
Insert Into game(game_id,user_id) Values(1,1)*** (1) HOLDS THE LOCK(S):
RECORD LOCKS space id 77 page no 18 n bits 624 index unique_gameId_userId of table `demo`.`game` trx id 3701497 lock mode S locks gap before rec
Record lock, heap no 237 PHYSICAL RECORD: n_fields 3; compact format; info bits 00: len 8; hex 8f5b6f9141c00000; asc  [o A   ;;1: len 8; hex 8026440340000040; asc  &D @  @;;2: len 8; hex 8f981c8273000000; asc     s   ;;*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 77 page no 18 n bits 624 index unique_gameId_userId of table `demo`.`game` trx id 3701497 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 237 PHYSICAL RECORD: n_fields 3; compact format; info bits 00: len 8; hex 8f5b6f9141c00000; asc  [o A   ;;1: len 8; hex 8026440340000040; asc  &D @  @;;2: len 8; hex 8f981c8273000000; asc     s   ;;*** (2) TRANSACTION:
TRANSACTION 3701499, ACTIVE 21 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 4 lock struct(s), heap size 1128, 2 row lock(s), undo log entries 1
MySQL thread id 43945, OS thread handle 22399490520832, query id 225853305 122.225.228.122 admin update
Insert Into game(game_id,user_id) Values(1,1)*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 77 page no 18 n bits 624 index unique_gameId_userId of table `demo`.`game` trx id 3701499 lock mode S locks gap before rec
Record lock, heap no 237 PHYSICAL RECORD: n_fields 3; compact format; info bits 00: len 8; hex 8f5b6f9141c00000; asc  [o A   ;;1: len 8; hex 8026440340000040; asc  &D @  @;;2: len 8; hex 8f981c8273000000; asc     s   ;;*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 77 page no 18 n bits 624 index unique_gameId_userId of table `demo`.`game` trx id 3701499 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 237 PHYSICAL RECORD: n_fields 3; compact format; info bits 00: len 8; hex 8f5b6f9141c00000; asc  [o A   ;;1: len 8; hex 8026440340000040; asc  &D @  @;;2: len 8; hex 8f981c8273000000; asc     s   ;;*** WE ROLL BACK TRANSACTION (2)

从错误信息里,发现是事务3701497和3701499产生了死锁,我们简称事务1和事务2。

事务1里持有了unique_game_id_user_id这个索引上624这个位置的记录的共享间隙锁。它在等待unique_game_id_user_id索引同一个位置的插入意向锁,这是排他间隙锁的一种类型。

事务2的锁状态和事务1一样,也是获取了同个索引位置共享间隙锁,在等待这个位置的排他间隙锁,所以它们陷入了锁死。

哪来的共享锁?

从死锁日志描述里存在一个共享间隙锁,但代码里的查询语句:select * from game where user_id = xx and game_id = xx。没有加任何锁,也不会占用共享间隙锁。

既然问题不是出在select上,那就是在insert上了。

在Mysql官方文档里描述,insert语句插入的加锁过程是这样:

  1. 插入意向间隙锁。加意向锁而不是排它间隙锁的好处是,在并发插入 时候,如果插入行没有重复就不会相互阻塞。
  2. 获得意向锁之后,如果插入行没有重复,则获得行锁,进行插入操作。
  3. 如果插入行重复了,就尝试获取一个共享的临键锁。这时,间隙锁获取成功,等待获取行的读锁。
  4. 如果上个一个事务提交,则会释放这行的行锁。当前事务就成功获得共享行锁,写入的时候提示行重复。
  5. 如果上一个事务回滚。则会释放这行的行锁。另外行不存在了,临键锁退化为间隙锁。当前事务就成功获得间隙锁,再申请插入意向锁,记录插入成功,再获得行锁。

共享锁来自并发insert

再回到上面的死锁日志,里面描述的共享间隙锁其实是insert语句执行时,发现有重复记录,那么当前事务会尝试获得的共享锁。

完整的逻辑是这样的,有三个事务同时执行insert。事务1执行成功后获得了该记录的行锁,但是事务没提交,所以行锁没释放。事务2执行插入,发现记录冲突了,会尝试获取共享临键锁,因为事务1行锁未释放,事务2只能获取临键锁中的间隙锁,无法获得行锁。所以当前事务要排队等待获取共享行锁。事务3也是一样的逻辑。

这时,事务1没有提交,而是因为某些原因回滚了事务。接着事务2和3排队等待的共享行锁都不需要了,有之前获得的间隙锁就行。然后它们又一起去获取排它的插入意向锁,插入意向锁和间隙锁是互斥的,其他事务未释放间隙锁,当前事务就无法获得插入意向锁。这样,事务2个3的间隙锁和插入意向锁形成了死锁。

小结

至此,死锁的原因是找到了,因为并发原因导致同一个请求被多次触发。多个事务同时执行同样的insert,在第一个事务回滚时,导致出现死锁。

在官方文档的描述里,除了执行三个insert会有死锁,先执行delete再执行三个insert也会出现死锁。原因是一样的,第一个事务执行delete后,其他事务因为锁冲突,也会先拿到共享锁。等第一个事务回滚后,就产生死锁。

当出现死锁时,我们可以通过show engine innodb statue命令查看死锁信息。当然也可以打开全局的死锁日志,将每次死锁信息都记录下来。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/586608.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

optimizer.load_state_dict()报错parameter group不匹配的问题的原因

在加载预训练权重时可能会遇到类似下面的错误: optimizer.load_state_dict(checkpoint[optimizer_state]) File "/opt/conda/lib/python3.8/site-packages/torch/optim/optimizer.py", line 145, in load_state_dict raise ValueError("loade…

Visual Studio Markdown Editor 插件导出 HTML

Visual Studio Markdown Editor 可通过右键弹出菜单选择“另存为”,轻松导出HTML文件或是单一的mhtml文件。然而,这个插件在导出HTML文件时有一个小问题,就是md文件中的一些内部链接无法在导出的HTML文件中正常工作。 其中的原因是&#xff…

Docker单点部署Seata(2.0.0) + Nacos(v2.3.0) + Mysql(5.7)

文章目录 一、部署Nacos二、部署Mysql三、Seata准备工作1. 记住nacos、mysql、宿主机的ip2. 建立数据库3. Nacos远程配置文件 四、部署Seata五、初步检验Seata部署情况六、微服务使用Seata1.引入依赖2. application.yml配置 七、遇到的坑1. Nacos显示Seata服务的ip为容器内网ip…

使用SpringBoot AOP记录操作日志和异常日志

使用SpringBoot AOP记录操作日志和异常日志 平时我们在做项目时经常需要对一些重要功能操作记录日志,方便以后跟踪是谁在操作此功能;我们在操作某些功 能时也有可能会发生异常,但是每次发生异常要定位原因我们都要到服务器去查询日志才能找…

第3课 获取并播放音频流

本课对应源文件下载链接: https://download.csdn.net/download/XiBuQiuChong/88680079 FFmpeg作为一套庞大的音视频处理开源工具,其源码有太多值得研究的地方。但对于大多数初学者而言,如何快速利用相关的API写出自己想要的东西才是迫切需要…

【机器学习】卷积神经网络(一)

一、网络结构 典型CNN结构 卷积神经网络是一种能够从图像、声音或其他类型的数据中学习特征的人工智能模型。你可以把它想象成一个有很多层的过滤器,每一层都能够提取出数据中的一些有用的信息,比如边缘、形状、颜色、纹理等。这些信息可以帮助卷积神经网…

ES高级用法:DeleteByQueryRequest

背景 在Elasticsearch中,delete_by_query API 允许你基于查询条件删除文档。在Java中,你可以使用Elasticsearch的Rest High Level Client或者Transport Client来执行这个操作。 示例代码 下面是使用Rest High Level Client进行delete_by_query操作的一…

【Matlab】ELM极限学习机时序预测算法

资源下载: https://download.csdn.net/download/vvoennvv/88681649 一,概述 ELM(Extreme Learning Machine)是一种单层前馈神经网络结构,与传统神经网络不同的是,ELM的隐层神经元权重以及偏置都是随机产生的…

【Android12】Android Framework系列---tombstone墓碑生成机制

tombstone墓碑生成机制 Android中程序在运行时会遇到各种各样的问题,相应的就会产生各种异常信号,比如常见的异常信号 Singal 11:Segmentation fault表示无效的地址进行了操作,比如内存越界、空指针调用等。 Android中在进程(主要…

Apache-ActiveMQ 反序列化漏洞(CVE-2015-5254)复现

CVE-2016-3088 一、环境搭建 Java:jdk8 影响版本 Apache ActiveMQ < 5.13.0 二、用docker搭建漏洞环境 访问一下web界面 然后进入admin目录登录 账号:admin 密码:admin 三、工具准备 cd /opt wget https://github.com/matthiaskaiser/jmet/releases/download/0.1.0/jmet-0…

QT上位机开发(第一个应用)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 不管是软件&#xff0c;还是硬件&#xff0c;如果我们能够顺利启动第一个应用&#xff0c;点亮第一个电路的话&#xff0c;这对我们的信心来说会有…

如何恢复 iPhone 上永久删除的照片?

2007年&#xff0c;苹果公司推出了一款惊天动地的智能手机&#xff0c;也就是后来的iPhone。你会惊讶地发现&#xff0c;迄今为止&#xff0c;苹果公司已经售出了 7 亿部 iPhone 设备。根据最新一项调查数据&#xff0c;智能手机利润的 95% 都进了苹果公司的腰包。 如此受欢迎…

多态的底层实现原理和泛型的底层实现原理

Java 多态的底层原理 - 知乎 (zhihu.com) 使用的是动态绑定&#xff0c;在调用这个方法的时候先去找实例的类&#xff0c;看是否有权限访问&#xff0c;并且看是否实现了该方法&#xff0c;没有的话就去父类找&#xff0c;为了提升效率&#xff0c;虚拟机不会每次都一层一层的…

【Vue2+3入门到实战】(16)VUEVue路由的重定向、404、编程式导航、path路径跳转传参 详细代码示例

目录 一、Vue路由-重定向1.问题2.解决方案3.语法4.代码演示 二、Vue路由-4041.作用2.位置3.语法4.代码示例 三、Vue路由-模式设置1.问题2.语法 四、编程式导航-两种路由跳转方式1.问题2.方案3.语法4.path路径跳转语法5.代码演示 path跳转方式6.name命名路由跳转7.代码演示通过n…

2023十大编程语言及未来展望

2023十大编程语言及未来展望 1. 2023年十大编程语言排行榜2. 十大编程语言未来展望PythonCCJavaC#JavaScriptPHPVisual BasicSQLAssembly language 1. 2023年十大编程语言排行榜 TIOBE排行榜是根据互联网上有经验的程序员、课程和第三方厂商的数量&#xff0c;并使用搜索引擎&a…

阿里云PolarDB数据库优惠价格表11元一天起

阿里云数据库PolarDB租用价格表&#xff0c;云数据库PolarDB MySQL版2核4GB&#xff08;通用&#xff09;、2个节点、60 GB存储空间55元5天&#xff0c;云数据库 PolarDB 分布式版标准版2核16G&#xff08;通用&#xff09;57.6元3天&#xff0c;阿里云百科aliyunbaike.com分享…

数据挖掘 聚类度量

格式化之前的代码&#xff1a; import numpy as np#计算 import pandas as pd#处理结构化表格 import matplotlib.pyplot as plt#绘制图表和可视化数据的函数&#xff0c;通常与numpy和pandas一起使用。 from sklearn import metrics#聚类算法的评估指标。 from sklearn.clust…

ansible管理windows测试

一、环境介绍 Ansible管理主机&#xff1a; 系统: redhat7.6 Linux管理服务器需安装pywinrm插件 Windows客户端主机&#xff1a; 系统: Server2012R2 Windows机器需要安装或升级powershell4.0以上版本&#xff0c;Server2008R2默认的版本是2.0&#xff0c;因此必须升…

k8s学习 — (DevOps实践)第十四章 微服务 DevOps 实战

k8s学习 — &#xff08;DevOps实践&#xff09;第十四章 微服务 DevOps 实战 ※ 各章节重要知识点1 项目构建1.1 项目环境1.2 服务 2 Jenkins CICD2.1 创建流水线项目2.2 Extended Choice Parameter 3 Kubesphere DevOps ※ 各章节重要知识点 k8s学习 — 各章节重要知识点 1…

使用flutter开发windows桌面软件读取ACR22U设备的nfc卡片id,5分钟搞定demo

最近有个需求&#xff0c;要使用acr122u读卡器插入电脑usb口&#xff0c;然后读取nfc卡片的id&#xff0c;并和用户账号绑定&#xff0c;调研了很多方式&#xff0c;之前使用rust实现过一次&#xff0c;还有go实现过一次&#xff0c;然后使用electron的时候遇到安装pcsc-lite失…