揭开MySQL并发中的“死锁”之谜:从原理到解决方案的深度解析
目录
- 1. 环境准备:创建“账户”和“标记”表
- 1.1 创建 `dl_account_t` 表
- 1.2 创建 `dl_mark_t` 表
- 2. 死锁详解
- 2.1 死锁情景一:相反加锁顺序导致的死锁
- 2.2 死锁情景二:唯一索引冲突引发的死锁
- 3. 事务隔离级别与锁机制
- 4. 预防与解决死锁的方法
- 4.1 预防死锁
- 4.2 处理死锁
- 5. 实践中的最佳实践
- 6. 总结
- 参考
在数据库的并发世界中, 死锁就像是交通高峰期的两辆车相向而行,彼此堵在十字路口,谁也不肯后退,导致交通完全停滞。死锁不仅会导致系统性能下降,还可能引发数据处理的中断。因此,深入理解死锁的成因及其解决方法对于数据库管理至关重要。本文详细解析两种常见的死锁情景,并探讨预防和解决死锁的方法。
1. 环境准备:创建“账户”和“标记”表
首先,我们有两个表:dl_account_t
和 dl_mark_t
。这些表模拟了不同的业务场景,通过这些场景,我们可以更直观地理解死锁的发生机制。
1.1 创建 dl_account_t
表
DROP TABLE IF EXISTS `dl_account_t`;
CREATE TABLE `dl_account_t` (`id` INT(11) NOT NULL AUTO_INCREMENT,`name` VARCHAR(255) DEFAULT NULL,`money` INT(11) DEFAULT 0,`account_id` INT(11) DEFAULT 0,PRIMARY KEY (`id`),UNIQUE `uk_account_id` (`account_id`)
) ENGINE=INNODB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;INSERT INTO `dl_account_t`(`name`,`money`,`account_id`)
VALUES ('C', 1000, 10001),('B', 1000, 10002),('A', 1000, 10003),('ZZ', 1000, 10005);
1.2 创建 dl_mark_t
表
DROP TABLE IF EXISTS `dl_mark_t`;
CREATE TABLE `dl_mark_t` (`a` INT(11) NOT NULL DEFAULT '0',`b` INT(11) DEFAULT NULL,PRIMARY KEY (`a`),UNIQUE KEY `uk_b` (`b`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;INSERT INTO `dl_mark_t` VALUES (1,1),(5,4),(20,20),(25,12);
2. 死锁详解
死锁发生在两个或多个事务相互等待对方持有的资源,导致所有相关事务无法继续执行。接下来,通过两个具体的例子,深入探讨死锁的成因和表现。
2.1 死锁情景一:相反加锁顺序导致的死锁
场景描述:
假设有两个事务同时操作 dl_account_t
表中的不同记录,但它们以相反的顺序加锁,导致彼此等待对方释放锁,最终形成死锁。
代码解析:
-- 设置事务隔离级别为 REPEATABLE READ
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN-- 事务1:减少id=1的moneyUPDATE `dl_account_t` SET `money` = `money` - 100 WHERE `id` = 1;-- 事务2:减少id=2的money-- UPDATE `dl_account_t` SET `money` = `money` - 100 WHERE `id` = 2;-- 事务1:增加id=2的moneyUPDATE `dl_account_t` SET `money` = `money` + 100 WHERE `id` = 2;-- 事务2:减少id=1的money-- UPDATE `dl_account_t` SET `money` = `money` - 100 WHERE `id` = 1;ROLLBACK;
执行步骤:
- 事务1开始,锁定
id=1
的记录,准备减少其money
。 - 事务2开始,尝试锁定
id=2
的记录,准备减少其money
。 - 事务1继续,尝试锁定
id=2
的记录,以增加其money
。但此时,id=2
已被事务2锁定,事务1进入等待状态。 - 事务2继续,尝试锁定
id=1
的记录,以减少其money
。然而,id=1
已被事务1锁定,事务2也进入等待状态。 - 形成循环等待,导致死锁。
可视化比喻:
想象两位银行职员同时处理不同账户的转账操作。职员A首先锁定账户1,准备扣款;职员B同时锁定账户2,准备扣款。随后,职员A需要锁定账户2以完成转账,而职员B需要锁定账户1。这时,两位职员互相等待对方释放锁,无法继续操作,最终陷入僵局。
解决办法:
- 统一加锁顺序:确保所有事务以相同的顺序访问和锁定资源。例如,始终先锁定较小的
id
再锁定较大的id
。 - 缩短事务时间:尽量减少事务中持有锁的时间,避免长时间占用资源。
- 使用适当的隔离级别:选择合适的事务隔离级别,平衡并发性能和一致性需求。
2.2 死锁情景二:唯一索引冲突引发的死锁
场景描述:
在dl_mark_t
表中,字段b
设置了唯一索引。当多个事务尝试插入具有相同或相邻b
值的记录时,如果处理不当,可能导致死锁。
代码解析:
BEGIN-- 死锁事务2INSERT INTO `dl_mark_t` VALUES (26,10);-- 死锁事务1INSERT INTO `dl_mark_t` VALUES (30,10);-- 死锁事务2INSERT INTO `dl_mark_t` VALUES (40,9);
死锁情况一:
- 事务2尝试插入
b=10
的记录,数据库检查唯一约束,锁定相应的索引范围(锁定b=10
)。 - 事务1同时尝试插入
b=10
,由于唯一约束冲突,数据库在等待事务2释放锁。 - 事务2接着尝试插入
b=9
,需要锁定b=9
的索引范围。然而,事务1已经持有相关锁,导致事务2进入等待状态。 - 形成循环等待,导致死锁。
死锁情况二:
BEGIN-- 死锁事务1INSERT INTO `dl_mark_t` VALUES (27, 29);-- 死锁事务2INSERT INTO `dl_mark_t` VALUES (28, 29);-- 死锁事务3INSERT INTO `dl_mark_t` VALUES (29, 29);ROLLBACK;
- 事务1尝试插入
b=29
,锁定对应的索引范围。 - 事务2尝试插入相同的
b=29
,进入等待状态。 - 事务3也尝试插入
b=29
,同样进入等待状态。 - 事务1回滚,释放锁。事务2和事务3继续执行,但由于相同的
b=29
值,可能继续产生锁冲突,进一步导致死锁。
可视化比喻:
设想多个服务员同时为不同顾客准备带有相同配料的菜品。服务员A先锁定配料10,准备菜品;服务员B也锁定配料10,准备菜品。这时,服务员A需要配料9,而服务员B也需要配料10,但被服务员A锁定,形成僵局。
解决办法:
- 使用唯一性检查:在应用层面预先检查唯一约束,避免重复插入导致的锁冲突。
- 序列化插入操作:通过队列或其他机制,确保同一时间只有一个事务进行特定范围的插入操作。
- 优化索引设计:合理设计索引,减少锁冲突的概率。
3. 事务隔离级别与锁机制
理解事务隔离级别和锁机制对于预防死锁至关重要。以下是常见的事务隔离级别及其在锁机制中的表现:
-
READ UNCOMMITTED:
- 特点:最低的隔离级别,允许脏读、不可重复读和幻读。
- 锁机制:最少的锁定,几乎不加锁,极易发生并发异常。
-
READ COMMITTED:
- 特点:防止脏读,但仍允许不可重复读和幻读。
- 锁机制:在读取数据时使用共享锁(S锁),在写入数据时使用排他锁(X锁)。
-
REPEATABLE READ:
- 特点:防止脏读和不可重复读,但在某些实现下仍可能允许幻读。
- 锁机制:通过行级锁和间隙锁(Gap Locks)防止幻读,InnoDB默认使用此级别。
-
SERIALIZABLE:
- 特点:最高的隔离级别,完全防止脏读、不可重复读和幻读。
- 锁机制:事务之间完全串行化执行,使用更严格的锁定策略,性能开销较大。
在上述死锁情景中,REPEATABLE READ
隔离级别下,由于间隙锁的存在,锁冲突的概率增加,从而更容易导致死锁。
4. 预防与解决死锁的方法
尽管死锁是并发控制中难以完全避免的问题,但通过合理的设计和策略,可以显著降低死锁的发生概率,并有效处理已发生的死锁。
4.1 预防死锁
-
统一资源访问顺序:
- 确保所有事务以相同的顺序访问和锁定资源,避免循环等待。例如,始终先锁定
id=1
再锁定id=2
。
- 确保所有事务以相同的顺序访问和锁定资源,避免循环等待。例如,始终先锁定
-
缩短事务时间:
- 尽量减少事务中持有锁的时间,避免长时间占用资源。将复杂的计算和逻辑放在事务之外执行,仅在事务中进行必要的数据库操作。
-
合理设计索引:
- 通过优化索引设计,减少锁的粒度和范围,降低锁冲突的可能性。
-
使用适当的隔离级别:
- 根据业务需求选择合适的事务隔离级别,平衡数据一致性和并发性能。对于高并发系统,可能需要在性能和一致性之间做出权衡。
4.2 处理死锁
-
检测与回滚:
- 数据库管理系统(DBMS)通常具备死锁检测机制。当检测到死锁时,会自动回滚其中一个事务,释放锁资源。应用程序应捕获死锁异常,适时重试事务操作。
-
重试机制:
- 在事务因死锁被回滚后,应用程序可以设计自动重试机制,尝试重新执行事务,直至成功或达到重试次数限制。
-
日志记录与监控:
- 通过日志记录和监控工具,及时发现和分析死锁情况,优化数据库设计和事务逻辑,减少死锁发生的频率。
5. 实践中的最佳实践
在实际应用中,遵循以下最佳实践有助于有效管理和减少死锁问题:
- 合理设计事务边界:确保事务尽可能简短,只包含必要的操作,避免在事务中执行耗时的任务。
- 避免长时间持有锁:在事务中尽量避免用户交互或其他可能导致锁长时间不释放的操作。
- 使用索引优化查询:通过合理的索引设计,优化查询性能,减少锁的竞争。
- 监控与分析死锁:定期监控数据库的死锁情况,分析其成因,针对性地优化应用程序和数据库设计。
6. 总结
死锁是并发控制中不可避免的挑战,但通过深入理解其成因、合理设计事务和锁机制,以及采用有效的预防和处理策略,可以显著降低死锁对系统性能和稳定性的影响。通过上述两个具体的死锁情景分析,展示了死锁在实际操作中的表现和成因,提供了针对性的解决方案。持续优化数据库设计和事务管理,将有助于构建高效、稳定的并发系统。
参考
0voice · GitHub
相关文章:
揭开MySQL并发中的“死锁”之谜:从原理到解决方案的深度解析
目录 1. 环境准备:创建“账户”和“标记”表1.1 创建 dl_account_t 表1.2 创建 dl_mark_t 表 2. 死锁详解2.1 死锁情景一:相反加锁顺序导致的死锁2.2 死锁情景二:唯一索引冲突引发的死锁 3. 事务隔离级别与锁机制4. 预防与解决死锁的方法4.1 …...

【论文阅读】Reliable, Adaptable, and Attributable Language Models with Retrieval
文章目录 OverviewCurrent Retrieval-Augmented LMsArchitectureTraining Limitations & Future Work Overview Parametic language models的缺点: 事实性错误的普遍存在验证的难度(可溯源性差)难以在有顾虑的情况下排除某些序列适应调整…...

A-Frame负责人Diego Marcos分享WebXR与开源AI的未来
一、引言 在最近的一次播客访谈中,《虚拟现实之声》的主持人Kent Bye与A-Frame的创始人Diego Marcos展开了一场关于WebXR技术及其未来发展潜力的对话。Diego不仅是A-Frame的创始人,同时也是WebXR规范的原创者之一。本次访谈不仅回顾了WebXR的发展历程,还探讨了开源AI技术在…...

【STM32-HAL库】火焰传感器(STM32F407ZGT6)(附带工程下载链接)
一、TEMT6000光照强度传感器 火焰传感器是一种能够检测火焰的传感器,它通过检测空气中的特定波长的光线来检测火焰的存在,并输出一个信号来通知系统发生了火灾 工作原理 火焰传感器的工作原理基于光学检测技术。当火焰燃烧时,会产生一些特…...
git merge没有生成合并提交
有时候本地使用gitmerge命令的时候会发现,合并后的log里边并没有一次merge branch的log,而是把合并分支上的所有commit log都带过来。 这是因为当执行合并操作时,如果目标分支的提交历史是源分支的直接延续(即,目标分支…...

算法题总结(十九)——图论
图论 DFS框架 void dfs(参数) { if (终止条件) {存放结果;return; }for (选择:本节点所连接的其他节点) {处理节点;dfs(图,选择的节点); // 递归回溯,撤销处理结果 } }深搜三部曲 确认递归函数,参数确认终止条件处理目前搜索节…...

android studio编译错误提示无法下载仓库
一、调整方法之一 buildscript {repositories {google()jcenter()//maven { url https://maven.aliyun.com/repository/google }//maven { url https://maven.aliyun.com/repository/central }}dependencies {// classpath "com.android.tools.build:gradle:4.1.1"c…...

基于SpringBoot的时装购物系统【源码】+【论文】
时装购物系统是一个基于Springboot框架开发的Web应用系统,数据库使用的是MySQL。该系统充分考虑了代码的可读性、实用性、扩展性和通用性,页面设计简洁、操作方便,易于后期维护。系统分为管理员和用户两大角色,前台页面提供了商品…...
自动化结账测试:使用 Playwright确保电商支付流程的无缝体验【nodejs]
使用 Playwright 掌握端到端结账测试 在电商领域,结账流程是用户体验中至关重要的一环。确保这一流程的稳定性和可靠性对于维护客户满意度和转化率至关重要。在本文中,我们将探讨如何使用 Playwright 进行端到端的结账测试,确保您的结账系统…...

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-25
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-25 0. 前言 大语言模型在很多领域都有成功的应用,在本期计算机前沿技术进展研究介绍中,我们将带来一篇用大语言模型进行诺贝尔文学作品分析的论文。虽然有一定趁最近诺贝尔奖热潮的意味&…...

【读书笔记-《网络是怎样连接的》- 5】Chapter2_4-网卡的工作过程
IP模块组装好的数据包,就可以交给网卡进行发送了。本篇就来介绍网卡在发送数据包时的工作过程。 1 以太网基础 以太网是一种为多台计算机能够彼此自由和廉价地相互通信而设计的通信技术,原型如下图所示。这种网络的本质其实是一根网线,通过…...

qt QOperatingSystemVersion详解
QOperatingSystemVersion 是 Qt 提供的一个类,用于表示和管理操作系统的版本信息。它允许开发者获取操作系统的名称、版本号和平台信息。这个类对于需要根据操作系统版本执行特定操作的应用程序尤其有用。 1. 构造函数 QOperatingSystemVersion(): 默认构造函数&…...

openpnp - 解决“底部相机高级校正成功后, 开机归零时,吸嘴自动校验失败的问题“
文章目录 openpnp - 解决"底部相机高级校正成功后, 开机归零时,吸嘴自动校验失败的问题"概述笔记问题现象1问题现象2原因分析现在底部相机和吸嘴的位置偏差记录修正底部相机位置现在再看看NT1在底部相机中的位置开机归零,看看是否能通过所有校…...

Python字幕滚动:为视频添加专业级动态效果!
Python实现由下向上滚动字幕 在数字媒体和编程领域,动态文本效果总能吸引观众的注意力。其中,滚动字幕是一种常见的视觉效果,经常用于视频、演示文稿和网页中。在Python中,我们可以通过多种方式来实现滚动字幕效果,比…...

Linux 系统中,将网络配置从 DHCP 改为静态 IP的几种方法
Linux 系统中,将网络配置从 DHCP 改为静态 IP 可以通过几种不同的方法来实现,下面是几种常见的方式: 方法一:使用 connman(Connection Manager) 如果你已经在使用 connman 管理网络,可以通过修…...
【jellyfin】解决Edge 浏览器播放 jellyfin 的 hevc/h265 视频“该客户端与媒体不兼容,服务器未发送兼容的媒体格式”错误
文章目录 问题原因分析解决方法 问题 在 windows 系统自带的 Edge 浏览器里网页播放 jellyfin 媒体库里的 hevc/h265 编码的视频时,总是提示 该客户端与媒体不兼容,服务器未发送兼容的媒体格式,无法播放视频。 原因分析 Edge 浏览器默认不…...

企业管理系统设计思路——毕业论文设计
根据企业对人事管理的要求,本系统可以实现以下目标: l 操作简单方便、界面简洁美观。 l 在查看员工信息时,可以对当前员工的家庭情况、培训情况进行添加、修改、删除的操作。 l 方便快捷的全方位数据查询。 l 按照指定的条件对员工进行统…...
Android 默认去掉URL网络校验,设置不进行网络校验
Android 系统连接WIFI显示网络连接受限分析处理_安卓13类原生系统网络受限-CSDN博客 package\modules\NetworkStack\src\com\android\networkstack\util\NetworkStackUtils.java public static final String CAPTIVE_PORTAL_MODE "captive_portal_mode"; //0 不…...

Python | Leetcode Python题解之第515题在每个树行中找最大值
题目: 题解: class Solution:def largestValues(self, root: Optional[TreeNode]) -> List[int]:if root is None:return []ans []q [root]while q:maxVal -inftmp qq []for node in tmp:maxVal max(maxVal, node.val)if node.left:q.append(n…...
Java泛型:类型安全的艺术
Java泛型是JDK 5中引入的一项重要特性,它为Java带来了类型安全的机制,极大地提升了代码的可读性和可维护性。泛型允许程序员在编译时检测非法类型,从而避免了运行时的ClassCastException异常,使得代码更加健壮和可靠。 泛型的基本…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...
Java求职者面试指南:计算机基础与源码原理深度解析
Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...
Caliper 配置文件解析:fisco-bcos.json
config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...

ubuntu22.04有线网络无法连接,图标也没了
今天突然无法有线网络无法连接任何设备,并且图标都没了 错误案例 往上一顿搜索,试了很多博客都不行,比如 Ubuntu22.04右上角网络图标消失 最后解决的办法 下载网卡驱动,重新安装 操作步骤 查看自己网卡的型号 lspci | gre…...
es6+和css3新增的特性有哪些
一:ECMAScript 新特性(ES6) ES6 (2015) - 革命性更新 1,记住的方法,从一个方法里面用到了哪些技术 1,let /const块级作用域声明2,**默认参数**:函数参数可以设置默认值。3&#x…...