MySQL插入更新死锁问题解析

news2024/9/24 17:17:22

文章目录

    • 1 问题背景
    • 2 线上问题
      • 2.1 线上异常日志
      • 2.2 数据准备
      • 2.3 问题复现
        • 2.3.1 执行流程
        • 2.3.2 死锁排查
      • 2.3.3 死锁日志分析
        • 2.3.3.1 事务A23087信息
        • 2.3.3.2 事务23087持有锁
        • 2.3.3.3 事务23087等待锁
        • 2.3.3.4 事务23088信息
        • 2.3.3.5 事务23088持有锁
        • 2.3.3.6 事务23088等待锁
    • 3 分析原因
    • 4 解决方法
    • 5 总结

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行可以看到异常的方法。
image

根据日志找到业务代码,发现业务代码逻辑是:先把回收单id 对应 历史的回收单都更新为失效,然后再插入回收单id对应的新的回收单数据。

2.2 数据准备

首先在测试库里建表,并准备相关的原数据。

1、使用的mysql版本:线上5.7.21,测试8.0.32

2、配置的隔离级别: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" ); #模拟线上数据

2.3 问题复现

2.3.1 执行流程
执行时间顺序事务A事务B
START TRANSACTION; START TRANSACTION;
1update checkout_detail SET remark = '更新状态' WHERE recycle_order_id = 30;
2update checkout_detail SET remark = '更新状态' WHERE recycle_order_id = 40;
3INSERT INTO checkout_detail ( id, recycle_order_id, confirm_recycle_time, contrast_type, remark )VALUES( 30, 30, '2024-07-15 19:56:01', 1, "插入回收单30" );
4INSERT 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;

它是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. 事务状态
    事务ID:23087
    操作:正在进行插入(INSERT)操作。
  2. 锁等待情况
    锁结构数量:3个锁结构,这表明MySQL为该事务准备了多个锁来管理对数据的访问。
    行锁数量:2个行锁,说明事务试图在checkout_detail表中的两行数据上设置锁。
  3. 事务阻塞
    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/>

事务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/>

事务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. 事务状态
    事务ID:23088
    操作:正在进行插入(INSERT)操作。
  2. 锁等待情况
    锁结构数量:3个锁结构,这表明MySQL为该事务准备了多个锁来管理对数据的访问。
    行锁数量:2个行锁,说明事务试图在checkout_detail表中的两行数据上设置锁。
  3. 事务阻塞
    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;;

事务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;;


事务23088正在等待一个插入意向锁(lock_mode X insert intention waiting)被授予

3 分析原因

参考MySQL的官方文档。

间隙锁(Gap Locks)是一种特殊的锁机制,用于锁定索引记录之间的间隙,或者第一个索引记录之前的间隙以及最后一个索引记录之后的间隙。这种锁的主要目的是防止其他事务在这些间隙中插入新的记录,从而维护数据的一致性和隔离性。

插入意向锁(Insert Intention Locks)
是InnoDB存储引擎在插入操作前设置的一种间隙锁(Gap Locks)。这种锁的目的是在多个事务尝试向同一个索引间隙中插入不同位置的数据时,能够并行执行而不需要相互等待。

可以得到索引如下加锁示意图

索引上添加锁

锁总是锁定索引记录。如果要锁定的是最后一条记录之后的区间,防止有人在这个区间插入数据,那么mysql就会锁定隐藏的最大记录

索引记录关联的锁

4 解决方法

1、查看死锁日志时,先看一下发生死锁的事务等待获取锁的语句,
都有哪些语句发生死锁。

2、根据死锁语句,找到相关到业务代码(如果有日志,直接根据日志找到业务代码也行)。

3、根据业务代码执行流程,来分析死锁发生过程。(注意分析数据存在,数据不存在时的加锁区别)

发现了问题原因,那么解决方案就很简单了。在这个场景下是:先查询数据是否存在,如果数据存在则更新,如果数据不存在再插入。

5 总结

  • 两个事务即使生成的间隙锁的范围是一样的,也不会发生冲突,因为间隙锁目的是为了防止其他事务插入数据,因此间隙锁与间隙锁之间是相互兼容的。

  • 在执行插入语句时,如果插入的记录在其他事务持有间隙锁范围内,插入语句就会被阻塞,因为插入语句在碰到间隙锁时,会生成一个插入意向锁,然后插入意向锁和间隙锁之间是互斥的关系。


关于作者
黄培祖 采货侠JAVA开发工程师

转转研发中心及业界小伙伴们的技术学习交流平台,定期分享一线的实战经验及业界前沿的技术话题。
关注公众号「转转技术」(综合性)、「大转转FE」(专注于FE)、「转转QA」(专注于QA),更多干货实践,欢迎交流分享~

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

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

相关文章

python进阶篇-day05-网络编程(TCP)与进程

day05网络编程 一. 网编三要素 ip 概述 设备(电脑, 手机, IPad, 耳机...)在网络中的唯一标识. 分类 按照 代数 划分: IPv4: 4字节, 十进制来表示, 例如: 192.168.13.157 IPv6: 8字节, 十六进制来表示, 理论上来讲, 可以让地球上的每一粒沙子都有自己的IP. Ipv4 常用类别划…

同三维S61-20SDI 20倍 HDMI SDI USB3.0 网口 3高清摄像机

同三维S61-20SDI 高清摄像机 20倍光学变焦&#xff0c; HDMI/SDI/USB3.0/网口&#xff0c;3.5音频口输入&#xff0c;350万像素 索尼机芯&#xff0c;支持POE&#xff0c;NDI HX2/Full NDI&#xff08;可定制&#xff09; 两款&#xff1a; S61-20SDI (不支持NDI) 单价&#…

1分钟 快速掌握 双向信号(inout信号)

​在数字电路设计中&#xff0c;三态门扮演着至关重要的角色。它是Verilog硬件描述语言中的一个基本元素&#xff0c;用于实现复杂电路的设计与模拟。 今天&#xff0c;我们一起来探讨三态门的基本原理、在Verilog中的实现方式。 一、什么是三态门? 三态门&#xff0c;简单…

fpga图像处理实战-边缘检测 (Roberts算子)

Roberts算子 Roberts算子是一种用于边缘检测的算子,主要用于图像处理中检测图像的边缘。它是最早的边缘检测算法之一,以其计算简单、速度快而著称。Roberts算子通过计算图像像素在对角方向的梯度来检测边缘,从而突出图像中灰度变化最剧烈的部分。 原理 Roberts算子通过…

力扣刷题(4)

正则表达式匹配 正则表达式匹配-力扣 思路来源&#xff1a;ithewei 若 *p 为空&#xff0c;*s 为空则匹配&#xff0c;*s 为非空则不匹配&#xff1b;当 *s为非空时&#xff0c;*p *s || *p ‘.’ 时第一个字符匹配&#xff1b;若 *(p1) ! ’ * 时&#xff0c;则递归判断…

python开发VTK入门

首先用pip命令安装VTK的python库&#xff1b; 需要一些时间&#xff0c;安装完成如下&#xff1b; 基本示例代码&#xff0c; import vtkcube vtk.vtkCubeSource() cube.SetXLength(100.0) cube.SetYLength(200.0) cube.SetZLength(300.0)mapper vtk.vtkPolyDataMapper() ma…

25届计算机毕业设计:3步打造北部湾助农平台,Java SpringBoot实践

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

《OpenCV计算机视觉》—— 图像轮廓检测与绘制

文章目录 一、轮廓的检测二、轮廓的绘制图像轮廓检测与绘制的代码实现 三、轮廓的近似 一、轮廓的检测 轮廓检测是指在包含目标和背景的数字图像中&#xff0c;忽略背景和目标内部的纹理以及噪声干扰的影响&#xff0c;采用一定的技术和方法来实现目标轮廓提取的过程注意:做轮…

探索Python中的Ellipsis:不仅仅是三个点

在Python 3.9中&#xff0c;Ellipsis 对象被赋予了一个新名称&#xff0c;即 ...&#xff0c;这使得它更容易输入和使用。这个变化是在Python 3.9版本中引入的&#xff0c;而不是3.1。这个变化的好处包括&#xff1a; 易用性&#xff1a;使用 ... 比输入 Ellipsis 更快&#xf…

第11讲 回环检测

1、理解回环检测的必要性 2、掌握基于词袋的外观式回环检测 3、通过DBoW3的实验&#xff0c;学习词袋模型的实际用途 1、概述 1.1 回环检测的意义 回环检测模块能够给出除了相邻帧的一些是个更加久远的约束。相机经过了同一个地方&#xff0c;采集了相似的数据。回环检测的关…

OpenCV 之图像平滑处理

引言 图像平滑处理&#xff08;也称为“模糊处理”&#xff09;是计算机视觉中一项非常基础的技术&#xff0c;常用于减少图像噪声或失真&#xff0c;提高图像质量。平滑处理可以通过各种滤波器实现&#xff0c;常见的滤波器包括均值滤波、方框滤波、高斯滤波和中值滤波。本文…

【赵渝强老师】大数据生态圈中的组件

大数据体系架构中的组件非常多&#xff0c;每个组件又属于不同的生态圈系统。从最早的Hadoop生态圈体系开始&#xff0c;逐步有了Spark生态圈体系和Flink生态圈体系。因此在学习大数据之前有必要了解一下每一个生态圈体系中具体包含哪些组件&#xff0c;以及它们的作用又是什么…

在移动应用程序中集成模糊方法的基于物联网的天气监测系统的实现

这篇论文的标题是《IMPLEMENTATION OF WEATHER MONITORING SYSTEM BASED INTERNET OF THINGS USING INTEGRATED FUZZY METHOD IN MOBILE APPLICATIONS》&#xff0c;作者是 Muhammad Malik Amin&#xff0c;来自 Politeknik Negeri Jakarta 的 D-IV INSTRUMENTASI DAN KONTROL …

WebAssembly内存结构学习记录

参考&#xff1a; 大文件上传深入研究&#xff1a;https://juejin.cn/post/6870837414852886542 WorkerWasm切片上传&#xff1a;https://juejin.cn/post/7221003401996091429 Wasm实现MD5文件编码&#xff1a;https://juejin.cn/post/7319541565318398003 SharedArrayBuffer与…

Python | 使用Pygments生成漂亮的代码段截图

在创建技术文档或教程时&#xff0c;包含代码段的图像以说明特定的示例或概念可能会有所帮助。但是&#xff0c;对代码文件进行屏幕截图可能看起来不专业&#xff0c;并且难以阅读。本文将探索如何使用库pygments将编程代码转换为Python中美丽的图像片段。 Pygments库 Pygmen…

基于Java+SpringBoot+Vue+MySQL的失物招领管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 基于SpringBootVue的失物招领管理系统【附源码文档】、前后…

Java 入门指南:Java 并发编程 —— 并发容器 LinkedBlockingQueue

BlockingQueue BlockingQueue 是Java并发包&#xff08;java.util.concurrent&#xff09;中提供的一个阻塞队列接口&#xff0c;它继承自 Queue 接口。 BlockingQueue 中的元素采用 FIFO 的原则&#xff0c;支持多线程环境并发访问&#xff0c;提供了阻塞读取和写入的操作&a…

JavaEE---Spring MVC(4)

MVC学习小案例1 在这里我们要实现一个计算器的功能 在这之前,先解决一个bug! 写好代码之后开始运行,运行发现不对,sum计算不出来,然后我百思不得其解, 1.对着后端代码一顿输出,还是没觉得有问题. 2.对着前端代码一顿输出,也没看出任何问题 3.是不是我前后端交互出错了呢?查找…

Python案例 | 四阶龙格库塔法简介

1.引言 在数值分析中&#xff0c;龙格-库塔法&#xff08;Runge-Kutta methods&#xff09;是用于非线性常微分方程的解的重要的一类隐式或显式迭代法。这些技术由数学家卡尔龙格和马丁威尔海姆库塔于1900年左右发明。 龙格-库塔(Runge-Kutta)方法是一种在工程上应用广泛的高…

工厂验收(FAT)和现场验收(SAT)的含义

工厂验收&#xff08;Factory Acceptance Test&#xff0c;FAT&#xff09;和现场验收&#xff08;Site Acceptance Test&#xff0c;SAT&#xff09;是在工程领域中常见的术语&#xff0c;用于确保设备在制造商及用户之间达成一致的验收标准&#xff0c;保证设备能够正常、安全…