什么是死锁?死锁是指两个或两个以上的事务在执行过程中,因争夺锁资源而造成的一种相互等待的现象,若无外力干预,这些事务都将无法进行下去。死锁的产生必须满足以下四个条件:

条件名称描述简单解释
互斥条件
(Mutual Exclusion)
一个资源每次只能被一个事务持有。锁是独占的,不能共享。
请求与保持条件
(Hold and Wait)
一个事务在持有至少一个资源的同时,又请求新的资源(该资源已被其他事务持有)。占着一个,还想要另一个。
不剥夺条件
(No Preemption)
事务已获得的资源在未使用完之前,不能被其他事务强行剥夺。已经拿到手的锁,别人不能抢走。
循环等待条件
(Circular Wait)
多个事务之间形成一种首尾相接的循环等待资源关系。事务A等事务B,事务B又在等事务A。

死锁发生以后,只有部分或完全回滚其中一个事务,才能打破死锁。多数情况下只需要重新执行因死锁回滚的事务即可。举个例子:

先准备一张表并查看其结构:

dotcpp_user表


dotcpp_user表结构

准备两个DOS窗口,分别命名A窗口、B窗口:

A窗口开启事务并执行:

BEGIN;
UPDATE dotcpp_user SET hobby = '刷题' WHERE  id = 1;

随即在B窗口里开启事务并执行:

BEGIN;
UPDATE dotcpp_user SET hobby = '刷网课' WHERE  id = 2;

由于id不同,非同行修改行为,不会出现锁等待。

为了看到死锁现象,于是你返回A窗口并执行:

UPDATE dotcpp_user SET hobby = '刷网课' WHERE  id = 2;

快速来到B窗口执行(只有50s,手速跟得上吧):

UPDATE dotcpp_user SET hobby = '刷题' WHERE  id = 1;

发生报错:

40001报错

通过:

SHOW ENGINE INNODB STATUS;

得到(仅部分展示):

2025-09-17 16:28:02 0x4af0
*** (1) TRANSACTION:
TRANSACTION 2427, ACTIVE 693 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1128, 3 row lock(s), undo log entries 1
MySQL thread id 42, OS thread handle 21172, query id 237 localhost ::1 Dotcpp updating
UPDATE dotcpp_user SET hobby = '?????' WHERE  id = 2
*** (1) HOLDS THE LOCK(S):
RECORD LOCKS space id 4 page no 4 n bits 72 index PRIMARY of table `dotcpp`.`dotcpp_user` trx id 2427 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 80000001; asc     ;;
 1: len 6; hex 00000000097b; asc      {;;
 2: len 7; hex 010000012b0151; asc     + Q;;
 3: len 12; hex 646f74637070557365723031; asc dotcppUser01;;
 4: len 6; hex e588b7e9a298; asc       ;;
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 4 page no 4 n bits 72 index PRIMARY of table `dotcpp`.`dotcpp_user` trx id 2427 lock_mode X locks rec but not gap waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 80000002; asc     ;;
 1: len 6; hex 00000000097c; asc      |;;
 2: len 7; hex 02000001270151; asc     ' Q;;
 3: len 12; hex 646f74637070557365723032; asc dotcppUser02;;
 4: len 9; hex e588b7e7bd91e8afbe; asc          ;;

由于交叉执行,窗口A、B尝试获取对方已经持有的那条记录的锁,从而形成了循环等待,即死锁。


如何检测死锁呢?

查看开闭状态参数:innodb_deadlock_detect;

死锁状态

默认情况是开启的。

ON:打开死锁检测(默认):用少量性能开销换取应用的稳定性和可预测性。系统会自动处理死锁,自动滚回其中某一事务,应用程序主要处理重试逻辑。

OFF:关闭死锁检测:用应用系统的复杂性和潜在故障风险来换取极致的性能。数据库不再处理死锁,依赖超时机制并通过锁等待来处理。

锁等待之前我们提到过,MySQL默认是50s,可以通过参数%innodb_lock_wait%进行查看:

默认锁等待时间是50s

要进行修改的话,参数是:innodb_lock_wait_timeout

举个例子,50s不符合我的习惯,60s就行了,我就局部设置一下为60s就好了:

SET session 
innodb_lock_wait_timeout= 60;

局部修改成60s

实际工作中,我们要尽量锁等待情况的发生,下面提供四种方法有效的方法:

核心要点方法
顺序访问并发操作多表或多行时,约定以相同顺序进行访问。
及时提交事务结束后立即提交或回滚,缩短锁持有时间。
一次锁定在同一事务中,一次性获取所有需要的资源锁。
锁粒度升级对易死锁场景,使用表级锁替代行级锁。


点赞(0)

C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:

一点编程也不会写的:零基础C语言学练课程

解决困扰你多年的C语言疑难杂症特性的C语言进阶课程

从零到写出一个爬虫的Python编程课程

只会语法写不出代码?手把手带你写100个编程真题的编程百练课程

信息学奥赛或C++选手的 必学C++课程

蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程

手把手讲解近五年真题的蓝桥杯辅导课程

Dotcpp在线编译      (登录可减少运行等待时间)