破局 MySQL 死锁:深入理解锁机制与高效解决方案

news2025/3/22 21:17:12

死锁的原理

1. 什么是死锁?

当 多个事务 在并发执行时,每个事务都 持有其他事务需要的锁,同时又在 等待对方释放锁,导致所有事务都无法继续执行的状态,称为 死锁(Deadlock)。

2. 死锁的四个必要条件

  • 互斥条件:资源(如某行数据)一次只能被一个事务独占。

  • 请求与保持条件:事务在持有某些锁的同时,请求新的锁。

  • 不剥夺条件:事务已获得的锁不能被强制剥夺。

  • 循环等待条件:事务之间形成 环形等待链,如事务A等待事务B,事务B又等待事务A。

MySQL 中死锁的常见场景

1. 场景1:交叉更新不同顺序

-- 事务1:先更新表A,再更新表B
BEGIN;
UPDATE table_a SET col = 1 WHERE id = 1;
UPDATE table_b SET col = 2 WHERE id = 2;
COMMIT;

-- 事务2:先更新表B,再更新表A
BEGIN;
UPDATE table_b SET col = 3 WHERE id = 2;
UPDATE table_a SET col = 4 WHERE id = 1;
COMMIT;
  • 事务1持有table_a.id=1的锁,请求table_b.id=2的锁。

  • 事务2持有table_b.id=2的锁,请求table_a.id=1的锁。

  • 形成循环等待,触发死锁。

2. 场景2:索引缺失导致全表锁

当 SQL 语句的 WHERE 条件字段无索引 时,InnoDB 引擎无法通过索引快速定位目标行,必须通过 全表扫描 逐行检查数据。在此过程中,InnoDB 会对 所有扫描到的行加锁(具体锁类型由隔离级别决定)。这种机制会导致以下问题:

锁范围扩大:即使实际需要修改的行很少,也可能因全表扫描锁定大量无关行。

间隙锁扩散:在可重复读(REPEATABLE READ)隔离级别下,InnoDB 会为全表扫描的行加上 间隙锁,锁定整个表的间隙。

锁冲突概率激增:多个事务并发执行全表扫描操作时,可能因锁竞争导致死锁。

场景示例

假设有一张 users 表,存储用户信息,其中 age 字段无索引:

CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    age INT,          -- 无索引
    status VARCHAR(20)
);

两个事务并发执行以下操作:

-- 事务1:更新年龄大于30的用户状态
BEGIN;
UPDATE users SET status = 'inactive' WHERE age > 30;  -- age字段无索引

-- 事务2:更新年龄小于20的用户状态
BEGIN;
UPDATE users SET status = 'active' WHERE age < 20;    -- age字段无索引

锁行为分析

事务1 执行 UPDATE 时,由于 age 无索引,InnoDB 必须 全表扫描,对所有扫描到的行加锁:

  • 若隔离级别为 可重复读(REPEATABLE READ),会加 Next-Key 锁(记录锁 + 间隙锁),锁定全表所有行及其间隙。
  • 若隔离级别为 读已提交(READ COMMITTED),仅加 记录锁,但全表扫描仍可能锁定大量行。

事务2 同样执行全表扫描,尝试锁定符合条件的行。若两事务锁定的行存在 交叉或重叠,可能导致相互等待,最终触发死锁。

3. 场景3:间隙锁(Gap Lock)冲突

隔离级别为可重复读(REPEATABLE READ) 时,InnoDB 会使用 间隙锁(锁定一个范围)。

例如:SELECT * FROM users WHERE id > 100 FOR UPDATE; 会锁定 id > 100 的所有间隙。

两个事务锁定不同的间隙范围时,可能因间隙交叉导致死锁。

 分析死锁

1. 查看死锁日志

执行以下命令获取死锁信息:

SHOW ENGINE INNODB STATUS;

在输出中查找 LATEST DETECTED DEADLOCK 部分,关键信息包括:

  • 涉及的事务:事务ID、执行的SQL语句。

  • 持有的锁:事务当前持有的锁类型(行锁、间隙锁等)。

  • 等待的锁:事务正在请求的锁。

2. 示例日志分析

LATEST DETECTED DEADLOCK
------------------------
2023-10-01 10:00:00 0x7f8e12345600
*** (1) TRANSACTION:
TRANSACTION 1001, ACTIVE 2 sec updating
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 1, OS thread handle 123456, query id 100 localhost root
UPDATE table_b SET col = 3 WHERE id = 2;

*** (1) HOLDS THE LOCK(S):
RECORD LOCKS space id 10 page no 3 n bits 72 index PRIMARY of table `test`.`table_b` 
trx id 1001 lock_mode X locks rec but not gap

*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 10 page no 4 n bits 72 index PRIMARY of table `test`.`table_a` 
trx id 1001 lock_mode X locks rec but not gap waiting

*** (2) TRANSACTION:
TRANSACTION 1002, ACTIVE 1 sec updating
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 2, OS thread handle 123457, query id 101 localhost root
UPDATE table_a SET col = 4 WHERE id = 1;

*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 10 page no 4 n bits 72 index PRIMARY of table `test`.`table_a` 
trx id 1002 lock_mode X locks rec but not gap

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 10 page no 3 n bits 72 index PRIMARY of table `test`.`table_b` 
trx id 1002 lock_mode X locks rec but not gap waiting
  • 务1 持有 table_b.id=2 的锁,等待 table_a.id=1 的锁。

  • 事务2 持有 table_a.id=1 的锁,等待 table_b.id=2 的锁。

  • 结论:典型的交叉更新死锁。

解决死锁

1. 统一资源访问顺序

  • 核心思想:所有事务按固定顺序访问资源(如先操作表A,再操作表B)。

  • 示例:修改事务2的更新顺序:

-- 事务2调整为先更新表A,再更新表B
BEGIN;
UPDATE table_a SET col = 4 WHERE id = 1;
UPDATE table_b SET col = 3 WHERE id = 2;
COMMIT;

2.优化索引

  • 避免全表扫描:为 WHERE 条件字段添加索引。

ALTER TABLE users ADD INDEX idx_age (age);

优化后,UPDATE users SET name = 'Tom' WHERE age = 20; 只会锁定符合条件的行,而非全表。

3. 缩短事务时间

  • 尽早提交事务:减少锁的持有时间。

  • 避免长事务:不在事务中执行耗时操作(如文件IO、网络请求)。

4. 调整隔离级别

  • 降低隔离级别:将隔离级别从 REPEATABLE READ 改为 READ COMMITTED,减少间隙锁的使用。

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

5.锁定读(FOR UPDATE)

  • 提前获取锁:在事务开始时锁定所有需要的资源。

  • 示例

BEGIN;
SELECT * FROM table_a WHERE id = 1 FOR UPDATE; -- 提前锁定行
UPDATE table_a SET col = 1 WHERE id = 1;
COMMIT;

6.使用锁超时

  • 设置 innodb_lock_wait_timeout(默认50秒),强制终止长时间等待锁的事务:

SET GLOBAL innodb_lock_wait_timeout = 30; -- 设置为30秒

补充

Mysql锁

一、锁的分类

1. 按锁的粒度划分
  • 全局锁:锁定整个数据库实例,用于全库备份。

  • 表级锁:锁定整张表,MyISAM 默认使用。

  • 行级锁:锁定表中的特定行,InnoDB 支持。

2. 按锁的兼容性划分
  • 共享锁(S锁):允许读,阻止写。

    SELECT * FROM table WHERE id = 1 LOCK IN SHARE MODE;
  • 排他锁(X锁):阻止读和写。

    SELECT * FROM table WHERE id = 1 FOR UPDATE;
3. 按锁的实现方式划分
  • 悲观锁:默认认为并发冲突会发生,先加锁再操作。

  • 乐观锁:假设冲突较少,通过版本号(如CAS)控制。

二、InnoDB 的行级锁类型

1. 记录锁(Record Locks)
  • 作用:锁定索引中的一行记录。

  • 场景:精确匹配索引的查询。

UPDATE users SET name = 'Tom' WHERE id = 1; -- 锁定 id=1 的行
2.间隙锁(Gap Locks)
  • 作用:锁定索引记录之间的间隙(范围,不包含记录本身)。

  • 场景:防止其他事务插入数据(解决幻读)。

SELECT * FROM users WHERE age > 20 FOR UPDATE; -- 锁定 age > 20 的间隙
3.临键锁(Next-Key Locks)
  • 作用:记录锁 + 间隙锁,锁定一个左开右闭的区间。

  • 场景:可重复读(REPEATABLE READ)下的默认锁机制。

SELECT * FROM users WHERE id BETWEEN 5 AND 10 FOR UPDATE; -- 锁定 (5,10]
4.插入意向锁(Insert Intention Locks)
  • 作用:标记一个间隙,表示事务准备在此插入数据。

  • 场景:插入新数据前触发,与间隙锁互斥。

INSERT INTO users (id, name) VALUES (6, 'Jerry'); -- 对 id=6 的间隙加插入意向锁

三、锁的兼容性

锁类型记录锁(X)间隙锁(Gap)临键锁(Next-Key)插入意向锁
记录锁(X)冲突冲突冲突冲突
间隙锁(Gap)兼容兼容冲突冲突
临键锁(Next-Key)冲突冲突冲突冲突
插入意向锁兼容冲突冲突兼容

四、不同隔离级别的锁行为

隔离级别脏读不可重复读幻读锁机制
READ UNCOMMITTED允许允许允许不加锁(仅快照读)
READ COMMITTED禁止允许允许仅记录锁,无间隙锁
REPEATABLE READ禁止禁止禁止记录锁 + 间隙锁(Next-Key Locks)
SERIALIZABLE禁止禁止禁止所有操作加锁,强制串行执行

当前读和快照读

当前读

当前读是指直接读取数据库中最新的已提交数据版本,并且会对读取的数据加锁(如行锁、间隙锁等),确保在事务结束前其他事务无法修改这些数据。 当前读(如 SELECT ... FOR UPDATEUPDATEDELETE)会通过 间隙锁(Gap Lock) 或 临键锁(Next-Key Lock) 锁定查询的范围,阻止其他事务在该范围内插入新数据。

触发场景:

SELECT ... FOR UPDATESELECT ... LOCK IN SHARE MODE

特性LOCK IN SHARE MODEFOR UPDATE
锁类型共享锁(S Lock)排他锁(X Lock)
其他事务能否加 S 锁✅ 是❌ 否
其他事务能否加 X 锁❌ 否❌ 否
典型用途保护数据不被修改,但允许并发读保护数据不被读或写,准备后续修改

解决的问题:

  • 解决的问题

    • 避免丢失更新(Lost Update)
      通过加锁保证事务在修改数据时,其他事务无法同时修改同一数据。

    • 保证数据强一致性
      确保事务中读取的是最新的数据版本。

    • 解决幻读

 快照读

快照读是基于 MVCC(多版本并发控制)机制读取数据的某个历史版本(通常是事务开始时的数据快照),不会对数据加锁。

触发场景:

普通 SELECT 语句(在 READ COMMITTED 或 REPEATABLE READ 隔离级别下)

解决问题:

  • 高并发读性能
    通过 MVCC 避免读-写冲突,读操作不会阻塞写操作。

  • 一致性视图
    在 REPEATABLE READ 隔离级别下,事务内的多次读取会基于同一个快照,保证可重复读

特性当前读快照读
数据版本最新已提交数据历史快照数据(基于事务开始时间点)
加锁加锁(行锁、间隙锁等)不加锁
触发语句SELECT ... FOR UPDATEUPDATEDELETE普通 SELECT
隔离级别所有隔离级别依赖隔离级别(RC 或 RR)
一致性强一致性(最新数据)最终一致性(历史数据)

 

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

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

相关文章

学习记录-cssjs-综合复习案例(二)

目录 商城复合案例功能实现&#xff08;二&#xff09;商城首页实现步骤1.准备工作2. 搭建html框架3. 编写js代码 完整实例代码完整项目心得 商城复合案例功能实现&#xff08;二&#xff09; 使用html&#xff0c;css&#xff0c;基于bootstrap框架以及媒体查询搭建响应式布局…

图解AUTOSAR_CP_EEPROM_Abstraction

AUTOSAR EEPROM抽象模块详细说明 基于AUTOSAR标准的EEPROM抽象层技术解析 目录 1. 概述 1.1 核心功能1.2 模块地位2. 架构概览 2.1 架构层次2.2 模块交互3. 配置结构 3.1 主要配置容器3.2 关键配置参数4. 状态管理 4.1 基本状态4.2 状态转换5. 接口设计 5.1 主要接口分类5.2 接…

汇川EASY系列之以太网通讯(MODBUS_TCP做从站)

汇川easy系列PLC做MODBUS_TCP从站,不需要任何操作,但是有一些需要知道的东西。具体如下: 1、汇川easy系列PLC做MODBUS_TCP从站,,ModbusTCP服务器默认开启,无需设置通信协议(即不需要配置),端口号为“502”。ModbusTCP从站最多支持31个ModbusTCP客户端(ModbusTCP主站…

QT 图表(拆线图,栏状图,饼状图 ,动态图表)

效果 折线图 // 创建折线数据系列// 创建折线系列QLineSeries *series new QLineSeries;// series->append(0, 6);// series->append(2, 4);// series->append(3, 8);// 创建图表并添加系列QChart *chart new QChart;chart->addSeries(series);chart->setTit…

基于vue框架的在线影院系统a079l(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;用户,电影,电影类别,电影库 开题报告内容 基于Vue框架的在线影院系统开题报告 一、研究背景与意义 随着文化娱乐产业的蓬勃发展&#xff0c;电影院作为人们休闲消遣的重要场所&#xff0c;其管理效率和服务质量直接影响着顾客的观影体…

OpenCV图像拼接(1)概述

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 此图说明了在Stitcher类中实现的拼接模块流程。使用该类&#xff0c;可以配置/移除某些步骤&#xff0c;即根据特定需求调整拼接流程。流程中的所…

基于ssm学科竞赛小程序的设计及实现(源码+lw+部署文档+讲解),源码可白嫖!

摘要 随着信息时代的来临&#xff0c;过去的学科竞赛管理方式的缺点逐渐暴露&#xff0c;本次对过去的学科竞赛管理方式的缺点进行分析&#xff0c;采取计算机方式构建学科竞赛小程序。本文通过阅读相关文献&#xff0c;研究国内外相关技术&#xff0c;提出了一种关于竞赛信息…

[特殊字符][特殊字符][特殊字符][特殊字符][特殊字符][特殊字符]壁紙 流光染墨,碎影入梦

#Cosplay #&#x1f9da;‍♀️Bangni邦尼&#x1f430;. #&#x1f4f7; 穹妹 Set.01 #后期圈小程序 琼枝低垂&#xff0c;霜花浸透夜色&#xff0c;风起时&#xff0c;微光轻拂檐角&#xff0c;洒落一地星辉。远山隐于烟岚&#xff0c;唯余一抹青黛&#xff0c;勾勒出天光水…

虚拟机的三种 Linux 网络配置原理图解读

前言 虚拟机的网络连接方式主要有 三种模式&#xff1a;桥接模式&#xff08;Bridged&#xff09;、NAT 模式&#xff08;Network Address Translation&#xff09;、仅主机模式&#xff08;Host-Only&#xff09;。每种模式都有不同的使用场景和网络适应性&#xff0c;具体解释…

AI Agent系列(七) -思维链(Chain of Thought,CoT)

AI Agent系列【七】 前言一、CoT技术详解1.1 CoT组成1.2 CoT的特点 二、CoT的作用三、CoT的好处四、CoT适用场景五、CoT的推理结构 前言 思维链(Chain of Thought,CoT)&#xff0c;思维链就是一系列中间的推理步骤(a series of intermediate reasoning steps)&#xff0c;通过…

SpringBoot实现异步调用的方法

在Java中使用Spring Boot实现异步请求和异步调用是一个常见的需求&#xff0c;可以提高应用程序的性能和响应能力。以下是实现这两种异步操作的基本方法&#xff1a; 一、异步请求&#xff08;Asynchronous Request&#xff09; 异步请求允许客户端发送请求后立即返回&#x…

PurpleLlama大模型安全全套检测方案

1. 引入 PurpleLlama是Meta的大模型安全整体解决方案&#xff08;参考1&#xff09;&#xff0c;它包括了 &#xff08;1&#xff09;安全评估 CyberSecEval是一个用于评估大型语言模型&#xff08;LLMs&#xff09;安全风险的基准套件&#xff0c;其目标是解决随着 LLMs 的广…

vue el-table 设置selection选中状态

toggleRowSelection 方法 vue el-table 设置selection选中状态 关键代码 multipleTableRef.value!.toggleRowSelection(item, true);<el-table:data"data":border"setBorder"v-bind"$attrs"row-key"id"stripestyle"width: 1…

STM32学习笔记之常用总线(原理篇)

&#x1f4e2;&#xff1a;如果你也对机器人、人工智能感兴趣&#xff0c;看来我们志同道合✨ &#x1f4e2;&#xff1a;不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 &#x1f4e2;&#xff1a;文章若有幸对你有帮助&#xff0c;可点赞 &#x1f44d;…

【数据结构】栈(Stack)、队列(Queue)、双端队列(Deque) —— 有码有图有真相

目录 栈和队列 1. 栈&#xff08;Stack&#xff09; 1.1 概念 1.2 栈的使用&#xff08;原始方法&#xff09; 1.3 栈的模拟实现 【小结】 2. 栈的应用场景 1、改变元素的序列 2、将递归转化为循环 3、逆波兰表达式求值 4、括号匹配 5、出栈入栈次序匹配 6、最小栈…

OpenCV中的矩阵操作

OpenCV中的矩阵操作主要围绕Mat类展开&#xff0c;涵盖创建、访问、运算及变换等。 1. 创建矩阵 ‌零矩阵/单位矩阵‌&#xff1a; Mat zeros Mat::zeros(3, 3, CV_32F); // 3x3浮点零矩阵 Mat eye Mat::eye(3, 3, CV_32F); // 3x3单位矩阵 自定义初始化‌&#xff1a…

OAK相机入门(一):深度测距原理

文章目录 1. 测距参数介绍2. 测距原理3. 总结 官方文档 Configuring Stereo Depth 1. 测距参数介绍 理论范围&#xff1a;0.2-35m 推荐范文&#xff1a;不低于0.5m 存储类型&#xff1a;uint16&#xff0c;0代表没有数据&#xff0c;或者测不到 2. 测距原理 通过视差进行测距…

Powershell WSL .wslconfig 实现与宿主机的网络互通

前言.wslconfig .wslconfig 用于在 WSL 2 上运行的所有已安装发行版中配置全局设置 wsl 2 网络模式介绍 Bridged (外部): 桥接模式将虚拟机的网络连接直接桥接到物理网络适配器上Mirrored (镜像): 镜像模式并不是一个标准的 Hyper-V 网络类型,但它通常指的是在网络适配器级…

Vue:Vue2和Vue3创建项目的几种常用方式以及区别

前言 Vue.js 和 Element UI 都是用 JavaScript 编写的。 1、Vue.js 是一个渐进式 JavaScript 框架。2、Element UI 是基于 Vue.js 的组件库。3、JavaScript 是这两个项目的主要编程语言。 而Element Plus是基于TypeScript开发的。 一、Vue2 1、基于vuecli工具创建 vue2 …

IRF拆除

冗余口、冗余组、备份组、虚墙、MAD检测、被控制器纳管、转换为安全策略 黑洞路由的定义: 有来无回的路由。 对设备拆除IRF操作流程。 1、关闭主框的业务口&#xff08;对设备的接口使用shutdown&#xff09;&#xff0c;关闭MAD检测口&#xff08;BFD/NQA/MAD&#xff09;&…