一篇学会MySQL死锁-更新插入导致死锁
时间:2025-11-05 09:34:41 出处:时尚阅读(143)
1 问题背景
前段时间,篇学领导说我们业务量大涨,死锁死锁部门新增HC,更新让我们赶紧招人。插入
领导:经过大家的篇学共同努力和不懈奋斗,我们的死锁死锁业务量实现了显著的大涨,这是更新对我们团队能力和工作成果的最好证明。为了更好地应对业务量的插入增长,满足客户的篇学需求,我们决定在部门内新增HC,死锁死锁大家行动起来吧。更新
。插入。篇学。死锁死锁
面试官:你好,更新今天想和你聊聊MySQL数据库中的死锁问题。首先,你能解释一下什么是死锁吗?
应聘者:死锁是指两个或多个事务在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,这些事务都将无法向前推进。在MySQL中,高防服务器这通常发生在多个事务尝试以不同顺序访问相同的资源(如表或行)时。
面试官:很好,那么MySQL中死锁发生的常见原因有哪些?
应聘者:MySQL中死锁的常见原因包括:
不同事务交叉锁定资源:当两个或多个事务相互等待对方释放锁定的资源时,就可能发生死锁。索引使用不当:不恰当的索引使用可能导致查询锁定更多行,增加死锁的风险。大量数据的修改:在处理大量数据时,尤其是同时修改多个表或行时,更容易发生死锁。锁升级:在某些情况下,低级锁(如行锁)可能会升级为更高级别的锁(如表锁),这也会增加死锁的可能性。
面试官:如何分析一个SQL都加了哪些锁呢?你需要哪些前置信息呢?
应聘者:好的,我先说一下我的理解。
加锁规则:两个原则、两个优化、一个 bug
原则 1:加锁的基本单位是 next-key lock,前开后闭区间
原则 2:查找过程中访问到的对象才会加锁
优化 1:索引上的免费源码下载等值查询,给唯一索引加锁的时候,匹配上数据,next-key lock 退化为行锁
优化 2:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock 退化为间隙锁
一个 bug:唯一索引上的范围查询会访问到不满足条件的第一个值为止
面试官:那我有个案例,你分析分析都加了哪些锁?是否会产生死锁。我有一个回收单表,回收单id+回收类型 是唯一索引, 我先根据回收单id A更新回收单A状态,(如果数据不存在)再插入回收单A数据。我再根据回收单id B更新回收单B状态,(如果数据不存在)再插入回收单B数据。
应聘者:。。。
上面是国内开发者在找工作过程中常被问到的问题,大家吐槽是八股文,过度依赖背诵,加剧应试教育的倾向,使得应聘者更加注重面试通过率而非实际技能的提升。源码下载
其实有些八股文是实际遇到问题的经验总结。
这个问题是我们在线上每日错误日志清零时发现排查的死锁问题。在这里介绍一下,给大家遇到类似问题时提供一个排查思路。
2 线上问题
2.1 线上异常日志
线上错误日志,从日志第2行可以发现是发生了死锁, 从第6行可以发现是插入了数据时发生了异常, 从20行可以看到异常的方法。
图片
根据日志找到业务代码,发现业务代码逻辑是:先把回收单id 对应 历史的回收单都更新为失效,然后再插入回收单id对应的新的回收单数据。
2.2 数据准备
首先在测试库里建表,并准备相关的原数据。1、使用的mysql版本:线上5.7.21,测试8.0.322、配置的隔离级别:REPEATABLE-READ 创建个checkout_detail表,分别插入三条数据。
复制CREATE TABLE `checkout_detail` ( `id` bigint(20) NOT NULL COMMENT 主键id, `recycle_order_id` bigint(20) NOT NULL COMMENT 回收单ID, `confirm_recycle_time` datetime NOT NULL COMMENT 确认回收时间, `contrast_type` int(4) NOT NULL COMMENT 对比类型:1:售前、2:后验、3:售后, `remark` varchar(255) DEFAULT COMMENT 备注, PRIMARY KEY (`id`), UNIQUE KEY `uniq_idx_recycle_order_id_contrast_type` (`recycle_order_id`,`contrast_type`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT=后验详情表; INSERT INTO checkout_detail ( id, recycle_order_id, confirm_recycle_time, contrast_type, remark ) VALUES ( 1, 1, 2024-07-15 19:56:01, 1, "回收单1" );#模拟线上数据 INSERT INTO checkout_detail ( id, recycle_order_id, confirm_recycle_time, contrast_type, remark ); VALUES ( 2, 10, 2024-07-15 19:56:01, 2, "回收单10" );#模拟线上数据 INSERT INTO checkout_detail ( id, recycle_order_id, confirm_recycle_time, contrast_type, remark ); VALUES ( 3, 20, 2024-07-15 19:56:01, 3, "回收单20" ); #模拟线上数据1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.2.3 问题复现
2.3.1 执行流程执行时间顺序
事务A
事务B
START TRANSACTION;
START TRANSACTION;
1
update checkout_detail SET remark = 更新状态 WHERE recycle_order_id = 30;
2
update checkout_detail SET remark = 更新状态 WHERE recycle_order_id = 40;
3
INSERT INTO checkout_detail ( id, recycle_order_id, confirm_recycle_time, contrast_type, remark )VALUES( 30, 30, 2024-07-15 19:56:01, 1, "插入回收单30" );
4
INSERT INTO checkout_detail ( id, recycle_order_id, confirm_recycle_time, contrast_type, remark )VALUES( 40, 40, 2024-07-15 19:56:01, 1, "插入回收单40" );
大家可以思考一下,这个执行过程中都会加哪些锁?会发生锁等待吗?会发生死锁吗?
2.3.2 死锁排查上面执行第3步会锁等待,执行第4步会死锁。
执行如下SQL:
复制SHOW ENGINE INNODB STATUS;1.它是MySQL 中一个非常有用的命令,它用于显示 InnoDB 存储引擎的当前状态信息。这个命令对于诊断 InnoDB 存储引擎的问题、监控性能以及理解内部操作非常有帮助。
输出的内容非常多,我们只关注锁信息就行,找到LATEST DETECTED DEADLOCK 最近一次死锁信息如下:
图片
2.3.3 死锁日志分析
现在让我们来分析这个死锁日志,我只会分析我们需要的信息。
2.3.3.1 事务A23087信息 复制*** (1) "TRANSACTION":<br/> TRANSACTION 23087, ACTIVE 22 sec inserting<br/> mysql tables in use 1, locked 1<br/> LOCK WAIT 3 lock struct(s), heap size 1128, 2 row lock(s), undo log entries 1<br/> MySQL thread id 9, OS thread handle 123145459134464, query id 1039 localhost 127.0.0.1 root update<br/> INSERT INTO checkout_detail ( id, recycle_order_id, confirm_recycle_time, contrast_type, remark )VALUES( 30, 30, 2024-07-15 "19":56:01, 1, "插入回收单30" )<br/>1.2.3.4.5.6. 事务状态 事务ID:23087 操作:正在进行插入(INSERT)操作。锁等待情况 锁结构数量:3个锁结构,这表明MySQL为该事务准备了多个锁来管理对数据的访问。行锁数量:2个行锁,说明事务试图在checkout_detail表中的两行数据上设置锁。事务阻塞 LOCK WAIT:这表明事务正在等待其他事务释放锁。在当前情况下,它正在等待能够插入或更新它试图操作的两行数据。2.3.3.2 事务23087持有锁 复制*** (1) HOLDS THE "LOCK(S)":<br/> RECORD LOCKS space id 4 page no 5 n bits 72 index uniq_idx_recycle_order_id_contrast_type of table `my_database`.`checkout_detail` trx id 23087 lock_mode X <br/> Record lock, heap no 1 PHYSICAL "RECORD": n_fields 1; compact format; info bits 0<br/> "0": len 8; hex 73757072656d756d; asc supremum;;<br/>1.2.3.4.事务23087持有的锁是一个针对uniq_idx_recycle_order_id_contrast_type索引的X(排他)锁,但它实际上锁定的是索引中的“supremum”伪记录。这通常意味着事务并没有锁定任何具体的、存在的数据行,而是锁定了一个表示索引末尾的虚拟记录。
2.3.3.3 事务23087等待锁 复制*** (1) WAITING FOR THIS LOCK TO BE "GRANTED":<br/> RECORD LOCKS space id 4 page no 5 n bits 72 index uniq_idx_recycle_order_id_contrast_type of table `my_database`.`checkout_detail` trx id 23087 lock_mode X insert intention waiting<br/> Record lock, heap no 1 PHYSICAL "RECORD": n_fields 1; compact format; info bits 0<br/> "0": len 8; hex 73757072656d756d; asc supremum;;<br/>1.2.3.4.事务23087正在等待一个插入意向锁(lock_mode X insert intention waiting)被授予
2.3.3.4 事务23088信息 复制** (2) "TRANSACTION": TRANSACTION 23088, ACTIVE 14 sec inserting mysql tables in use 1, locked 1 LOCK WAIT 3 lock struct(s), heap size 1128, 2 row lock(s), undo log entries 1 MySQL thread id 10, OS thread handle 123145460199424, query id 1043 localhost 127.0.0.1 root update INSERT INTO checkout_detail ( id, recycle_order_id, confirm_recycle_time, contrast_type, remark )VALUES( 40, 40, 2024-07-15 "19":56:01, 1, "插入回收单40" )1.2.3.4.5.6. 事务状态 事务ID:23088 操作:正在进行插入(INSERT)操作。锁等待情况 锁结构数量:3个锁结构,这表明MySQL为该事务准备了多个锁来管理对数据的访问。行锁数量:2个行锁,说明事务试图在checkout_detail表中的两行数据上设置锁。事务阻塞 LOCK WAIT:这表明事务正在等待其他事务释放锁。在当前情况下,它正在等待能够插入或更新它试图操作的两行数据。2.3.3.5 事务23088持有锁 复制*** (2) HOLDS THE "LOCK(S)": RECORD LOCKS space id 4 page no 5 n bits 72 index uniq_idx_recycle_order_id_contrast_type of table `my_database`.`checkout_detail` trx id 23088 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;;1.2.3.4.事务23088持有的锁是一个针对uniq_idx_recycle_order_id_contrast_type索引的X(排他)锁,但它实际上锁定的是索引中的“supremum”伪记录。这通常意味着事务并没有锁定任何具体的、存在的数据行,而是锁定了一个表示索引末尾的虚拟记录。
2.3.3.6 事务23088等待锁 复制*** (2) WAITING FOR THIS LOCK TO BE "GRANTED": RECORD LOCKS space id 4 page no 5 n bits 72 index uniq_idx_recycle_order_id_contrast_type of table `my_database`.`checkout_detail` trx id 23088 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;;1.2.3.4.事务23088正在等待一个插入意向锁(lock_mode X insert intention waiting)被授予
3 分析原因
参考MySQL的官方文档。
间隙锁(Gap Locks)是一种特殊的锁机制,用于锁定索引记录之间的间隙,或者第一个索引记录之前的间隙以及最后一个索引记录之后的间隙。这种锁的主要目的是防止其他事务在这些间隙中插入新的记录,从而维护数据的一致性和隔离性。
插入意向锁(Insert Intention Locks) 是InnoDB存储引擎在插入操作前设置的一种间隙锁(Gap Locks)。这种锁的目的是在多个事务尝试向同一个索引间隙中插入不同位置的数据时,能够并行执行而不需要相互等待。
可以得到索引如下加锁示意图
索引上添加锁
锁总是锁定索引记录。如果要锁定的是最后一条记录之后的区间,防止有人在这个区间插入数据,那么mysql就会锁定隐藏的最大记录
索引记录关联的锁
4 解决方法
1、查看死锁日志时,先看一下发生死锁的事务等待获取锁的语句, 都有哪些语句发生死锁。
2、根据死锁语句,找到相关到业务代码(如果有日志,直接根据日志找到业务代码也行)。
3、根据业务代码执行流程,来分析死锁发生过程。(注意分析数据存在,数据不存在时的加锁区别)
发现了问题原因,那么解决方案就很简单了。在这个场景下是:先查询数据是否存在,如果数据存在则更新,如果数据不存在再插入。
5 总结
两个事务即使生成的间隙锁的范围是一样的,也不会发生冲突,因为间隙锁目的是为了防止其他事务插入数据,因此间隙锁与间隙锁之间是相互兼容的。在执行插入语句时,如果插入的记录在其他事务持有间隙锁范围内,插入语句就会被阻塞,因为插入语句在碰到间隙锁时,会生成一个插入意向锁,然后插入意向锁和间隙锁之间是互斥的关系。猜你喜欢
- 以长城移动手机卡怎么样?(探究长城移动手机卡的优势和特点)
- 重温数据结构经典:HashCode及HashMap原理
- JavaScript 新功能:findLast() 和 findLastIndex()
- 什么!我们开发的计费系统把公司的钱算错了?
- 在LINUX中自带的中文输入法 一直不太友好,用的不够爽。最近SOGO开发出了UBUNTU下的SOGO,安装了下。SOGO的智能化 ,是目前用的最舒服的输入法。下面是个人详细的安装步骤以及遇到问题的解决方法。软件名称:搜狗输入法 for Linux 2.0.0.0066 中文官方安装版 32位软件大小:17.8MB更新时间:2015-10-19软件名称:搜狗输入法 for Linux 2.0.0.0066 中文官方安装版 64位软件大小:17.8MB更新时间:2015-10-191、可以使用本文上面提供的下载,也可以打开下图中的SOGO官网,下载搜狗输入法安装包sogou_pinyin_linux_1.1.0.0037_i386.deb ,注意选择32BIT系统的安装包。2、为什么选择32BIT,不选择64BIT的呢?因为我的UBUNTU2.04安装的是32BIT版本。那如何判断系统是32还是64呢?在终端下输入命令getconf LONG_BIT,可以获取当前系统BIT数[xxx@ ~]getconf LONG_BIT323、安装输入法DEB包前,需要升级系统的一些基本库4、执行安装命令[xxx@ ~]sudo dpkg -i sogou_pinyin_linux_1.1.0.0037_i386.deb 发现错误,提示/usr/lib/libfreetype.so.6不是软链接执行以下命令[xxx@ ~]sudo ln -sf /usr/lib/i386-linux-gnu/libfreetype.so.6 /usr/lib/libfreetype.so.6解决后再次执行安装DEB包命令[xxx@ ~]sudo dpkg -i sogou_pinyin_linux_1.1.0.0037_i386.deb这样就可以安装成功5、安装成功后,需要重启电脑,输入法会自动替换成SOGO,测试了下很爽,熟悉的WINDOW回来了,谢谢阅读,希望能帮到大家,请继续关注脚本之家,我们会努力分享更多优秀的文章。相关推荐:Ubuntu 14.10系统中IBUS 中文输入法安装的图文教程
- 关于防御性编程,你应该知道的事
- DataGuard单实例到RAC搭建
- 美国大厂码农薪资曝光:年薪18万美元,够养家,不够买海景房
- 双核电脑装Win7的优势与挑战(优化双核电脑性能,提升Win7系统使用体验)