前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >一个 MySQL 数据库死锁的案例和解决方案

一个 MySQL 数据库死锁的案例和解决方案

作者头像
mzlogin
发布于 2023-10-23 02:20:01
发布于 2023-10-23 02:20:01
38600
代码可运行
举报
文章被收录于专栏:闷骚的程序员闷骚的程序员
运行总次数:0
代码可运行

本文介绍了一个 MySQL 数据库死锁的案例和解决方案。

场景

生产环境出了一个偶现的数据库死锁问题,导致少部分业务处理失败。

分析特征之后,发现是多个线程并发执行同一个方法,更新关联的数据时可能会出现,把场景简化概括一下:

  • 有一个数据表 tb1,主键名 id,有两条 id 分别为 A1 和 A2 的记录,对应的外键 fk_biz_no 相同;
  • 方法 myFunc,整体是一个事务;
  • 方法 myFunc 里的逻辑是先更新 tb1 里的一条记录,执行一些逻辑后,再更新该记录的外键对应的所有记录;

这样 线程1 和 线程2 并发执行 myFunc 方法时,示意如下:

线程1 先更新 A1,此时会对 A1 所在行加写锁,再更新 A1 和 A2,此时会同时给 A1 和 A2 所在行都加上写锁;

线程2 先更新 A2,此时会对 A2 所在行加写锁,再更新 A1 和 A2,此时会同时给 A1 和 A2 所在行都加上写锁。

如此一来,如果出现类似以下的执行时序,则会形成死锁:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
sequenceDiagram
    participant 线程1
    participant 线程2
    线程1->>线程1: 更新 A1
    线程2->>线程2: 更新 A2
    rect rgba(191, 233, 255, 0.3)
        note right of 线程1: 线程1 持有 A1 写锁请求 A1A2 写锁,<br>线程2 持有 A2 写锁请求 A1A2 写锁,<br>死锁形成。
        线程1->>线程1: 更新 A1A2
        线程2->>线程2: 更新 A1A2
    end

带着一点伪装的 ABBA 死锁。

解决方案

按照消除死锁条件的思路,一般会想到将两个线程里的加锁顺序改为一致,但是此场景并不完全适用。以下是几种可行的方案:

方案一、对 myFunc 方法加分布式锁,可以用需要更新的记录的 fk_biz_no 作为锁的 key,这样同一个 fk_biz_no 的更新操作就会串行执行;

方案二、在方法/事务的最开始,就提前把 A1A2 的写锁申请到(比如 SELECT ... FOR UPDATE),然后再执行后续逻辑;

方案三、优化 myFunc 方法里的逻辑,先将 A1 和 A2 的数据都处理好了,然后一次性更新 A1A2,即将方法里的两次更新合并成一次更新;

方案一 和 方案二 效果类似,都是使同一 fk_biz_no 的更新操作串行了;而方法三则是消除了 ABBA 的情况(实际场景中有可能需要考虑并发执行下的数据混乱、数据覆盖的问题,那是另外的话题了,在此不展开)。

小结

来一起复习下死锁的四个必要条件:

  • 互斥条件:一个资源每次只能被一个进程使用;
  • 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放;
  • 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺;
  • 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

预防和消除死锁的思路,则无非是消除上述四个条件中的一个或多个。

文档信息

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023/08/31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Java 程序死锁问题原理及解决方案
Java 语言通过 synchronized 关键字来保证原子性,这是因为每一个 Object 都有一个隐含的锁,这个也称作监视器对象。在进入 synchronized 之前自动获取此内部锁,而一旦离开此方式,无论是完成或者中断都会自动释放锁。显然这是一个独占锁,每个锁请求之间是互斥的。相对于众多高级锁 (Lock/ReadWriteLock 等),synchronized 的代价都比后者要高。但是 synchronzied 的语法比较简单,而且也比较容易使用和理解。Lock 一旦调用了 lock() 方法获取到锁而未正确释放的话很有可能造成死锁,所以 Lock 的释放操作总是跟在 finally 代码块里面,这在代码结构上也是一次调整和冗余。Lock 的实现已经将硬件资源用到了极致,所以未来可优化的空间不大,除非硬件有了更高的性能,但是 synchronized 只是规范的一种实现,这在不同的平台不同的硬件还有很高的提升空间,未来 Java 锁上的优化也会主要在这上面。既然 synchronzied 都不可能避免死锁产生,那么死锁情况会是经常容易出现的错误,下面具体描述死锁发生的原因及解决方法。
哲洛不闹
2018/09/18
1K0
Java 程序死锁问题原理及解决方案
死锁的原因,和一般的解决方案
死锁是由四个必要条件导致的,所以一般来说,只要破坏这四个必要条件中的一个条件,死锁情况就应该不会发生。
海涛
2019/08/09
5.3K0
Oracle的常见问题汇总(3) ——​数据库死锁原因及解决办法
数据库死锁原因及解决办法 所谓死锁:是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。由于资源占用是互斥的,当某个进程提出申请资源后,使得有关进程在无外力协助下,永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象死锁。 一种情形,此时执行程序中两个或多个线程发生永久堵塞(等待),每个线程都在等待被其他线程占用并堵塞了的资源。例如,如果线程A锁住了记录1
用户1257215
2018/01/30
2.7K0
Oracle的常见问题汇总(3) ——​数据库死锁原因及解决办法
千万级MySQL数据库建立索引,提高性能的秘诀
实践中,MySQL的优化主要涉及SQL语句及索引的优化、数据表结构的优化、系统配置的优化和硬件的优化四个方面,如下图所示:
李红
2019/08/31
3.9K0
事务、锁、死锁
并发执行会打破事务的隔离性,根据破坏的程度分成可重复读、读已提交、读未提交等隔离级别,每种级别都决定了多线程并发时数据可见性不同,是并发和隔离性之间的权衡,也因此产生了幻读、不可重复读、脏读等问题。
冰寒火
2023/03/01
6440
【Linux】:线程安全 + 死锁问题
🎐 互斥条件:只有对必须互斥使用的资源的争抢才会导致死锁。 如果把只能互斥使用的资源改造为允许共享使用,则系统不会进入死锁状态。比如:SPOOLing 技术。操作系统可以采用 SPOOLing 技术 把独占设备在逻辑上改造成共享设备。比如,用SPOOLing 技术 将打印机改造为共享设备..
IsLand1314
2024/12/21
1610
【Linux】:线程安全 + 死锁问题
面试官:用SQL写一个死锁的案例
粉丝表示对Java中的死锁还是略知一二的,但是突然用SQL写死锁的案例之前还真没遇到过,这个问题没答上来。所以今天就带大家一起来看下怎么用SQL让数据库中产生死锁。
编程大道
2022/05/17
1.6K0
面试官:用SQL写一个死锁的案例
面试官:什么是死锁?如何解决死锁?写一段死锁的代码吧!
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。产生死锁的原因,主要包括:
CG国斌
2020/05/15
21.8K2
我通过六个 MySQL 死锁案例,终于理解了死锁的原因
生活中,最常见的案例之一,十字路口没有红绿灯,到了十字路口相互不让,最后,整个马路瘫痪,在我们技术层面称之为死锁。
田维常
2021/04/22
2.4K0
我通过六个 MySQL 死锁案例,终于理解了死锁的原因
Java死锁、活锁,悲观锁、乐观锁
死锁:是指两个或两个以上的进程(或线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。 
红目香薰
2022/11/29
4920
数据库知识整理
Read uncommitted:读未提交,顾名思义,就是一个事务可以读取另一个未提交事务的数据。
用户3467126
2019/10/10
8140
数据库知识整理
多线程死锁的产生以及如何避免死锁
多线程以及多进程改善了系统资源的利用率并提高了系统 的处理能力。然而,并发执行也带来了新的问题——死锁。所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。
全栈程序员站长
2022/09/14
1.1K0
多线程死锁的产生以及如何避免死锁
并发,又是并发
java 中的线程分为两种:守护线程(Daemon)和用户线程(User)。任何线程都可以设置为守护线程和用户线程,通过方法 Thread.setDaemon(boolon);true 则把该线程设置为守护线程,反之则为用户线程。Thread.setDaemon()必须在 Thread.start()之前调用,否则运行时会抛出异常。 两者的区别:唯一的区别是判断虚拟机(JVM)何时离开,Daemon 是为其他线程提供服务,如果全部的 User Thread 已经撤离,Daemon 没有可服务的线程,JVM 撤离。也可以理解为守护线程是 JVM 自动创建的线程(但不一定),用户线程是程序创建的线程;比如 JVM 的垃圾回收线程是一个守护线程,当所有线程已经撤离,不再产生垃圾,守护线程自然就没事可干了,当垃圾回收线程是 Java 虚拟机上仅剩的线程时,Java 虚拟机会自动离开。扩展:Thread Dump 打印出来的线程信息,含有 daemon 字样的线程即为守护进程,可能会有:服务守护进程、编译守护进程、windows 下的监听 Ctrl+break的守护进程、Finalizer 守护进程、引用处理守护进程、GC 守护进程。
cloudskyme
2020/06/15
1.1K0
手画图解 | 关于死锁,面试的一切都在这里了
死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象。若无外力作用,它们都将无法推进下去。
飞天小牛肉
2023/01/11
3780
死锁问题排查
最近有同事说平台的某个服务出现超时异常,让我帮忙看下原因。我进入平台后触发了该服务,并没有发现超时异常,那可能是在特定操作场景下会出现或者是一个非必现问题。跟同事沟通之后,找到了复现的流程。
数据小冰
2022/08/15
1.1K0
死锁问题排查
数据库遇到了死锁,你该如何排查?
前段时间做了很多慢SQL的优化工作,这周刚好又被反馈服务出现了死锁导致了业务报错。看了一下云数据库的告警日志,发现出现了比较多的事务未提交、死锁、等待行锁的严重警告。都是一些棘手的运维工作,涉及到业务流程的梳理、SQL的优化等工作。
chengcheng222e
2021/12/09
1.1K0
数据库遇到了死锁,你该如何排查?
不同数据库的特点_简述数据库的特点
文档由一组key value组成。文档是动态模式,这意味着同一集合里的文档不需要有相同的字段和结构。在关系型数据库中table中的每一条记录相当于MongoDB中的一个文档。
全栈程序员站长
2022/09/19
1.4K0
吃瓜是需要底层数据库事务锁支撑的
锁是MySQL在服务器层和存储引擎层的并发控制,锁可以保证数据并发访问的一致性、有效性;
Liusy
2021/02/01
5020
MySQL/InnoDB中,乐观锁、悲观锁、共享锁、排它锁、行锁、表锁、死锁概念的理解
MySQL/InnoDB的加锁,一直是一个面试中常问的话题。例如,数据库如果有高并发请求,如何保证数据完整性?产生死锁问题如何排查并解决?我在工作过程中,也会经常用到,乐观锁,排它锁,等。于是今天就对这几个概念进行学习,屡屡思路,记录一下。
程序员鹏磊
2018/07/30
2.7K0
死锁的 4 种排查工具 !
死锁(Dead Lock)指的是两个或两个以上的运算单元(进程、线程或协程),都在等待对方停止执行,以取得系统资源,但是没有一方提前退出,就称为死锁。
磊哥
2021/08/25
1.9K0
相关推荐
Java 程序死锁问题原理及解决方案
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验