MySQL死锁解决方法与永久避免方案
MySQL自动检测死锁并回滚代价最小的事务,无需人工干预。通过SHOWENGINEINNODBSTATUS查看死锁日志定位根源。永久避免需保持短事务、固定表访问顺序、加索引,并在业务代码中捕获死锁异常自动重试。
说到MySQL里的死锁,其实用一个场景就能说清楚:多个事务像是在十字路口互相抢路,谁都不肯让步,结果全部堵在路中间动弹不得。而在MySQL的底层设计里,系统会迅速察觉到这个僵局——它会主动选一个代价最小的事务“拖出去”,直接中断该事务并抛出一个死锁异常,另一个事务则顺利通行。这就是为什么我们在实际开发中,经常会收到类似“Deadlock found when trying to get lock”这样的报错信息。
这篇内容主要解决两个实际痛点:
- 已经发生死锁怎么办:也就是业务被死锁卡住时,如何快速恢复?
- 如何从根源上预防:让这种“交通堵塞”不再反复出现。

一、已经产生死锁:3步快速解决与排查
1. 无需手动杀进程!MySQL 内置死锁检测会自动处理
MySQL引擎内部自带一套死锁检测机制,默认处于开启状态。一旦监测到死锁:
- 它会立即回滚代价最小的那个事务
- 然后向应用层抛出一个
Deadlock found when trying to get lock; try restarting transaction异常 - 而另一边,另一个事务完全不受影响,继续正常执行——死锁瞬间被解除
所以第一件需要明确的事情是:死锁一旦发生,无需人工手动干预,MySQL自身就能在毫秒级把问题解决掉。
2. 查看死锁日志:精准定位根源(最关键的一步)
虽然MySQL帮你把堵住的车拖走了,但你绝不能忽略“到底是怎么堵的”这个核心问题。这才是真正需要重视的事情。定位死锁原因只需一条命令:
-- 查看最近一次死锁的详细信息SHOW ENGINE INNODB STATUS;
在返回的结果中,找到 LATEST DETECTED DEADLOCK 这一部分,它会清晰告诉你:
- 是哪两个事务纠缠在一起
- 各自执行的SQL语句是什么
- 各自持有哪些锁、又在等待哪些锁
- 死锁发生的具体时间
一句话总结:这是后续所有调优工作的唯一可靠依据。
3. 临时应急:如果事务卡住不自动解除怎么办?
有一种比较少见的情况,就是如果你手动关闭了死锁检测,事务就会一直挂在那里无法推进。此时你可以通过下面两个命令手动处理:
-- 1. 查看当前正在运行的事务,找到卡住的 IDSELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;-- 2. 强制杀掉卡住的事务KILL 事务ID;
二、永久避免死锁:4个核心方案(必做)
说实话,死锁在技术层面确实无法做到100%杜绝,但通过规范化的设计和良好的编码习惯,99%的死锁其实完全可以被有效控制。
1. 事务保持短小精悍,避免长事务
- 死锁发生的概率与事务执行时间成正比,这是一个很难绕过的规律
- 严禁在事务内部嵌套:网络请求、文件IO、人工操作或sleep等待
- 基本原则:快进快出,执行完毕立即提交或回滚
典型的反面案例:
BEGIN;UPDATE 表 SET ...;-- 这里调用了外部接口,耗时3秒,锁一直持有,极易引发死锁COMMIT;
3秒的耗时,在数据库世界里已经属于“长事务”了。
2. 所有表固定访问顺序(最有效的策略)
死锁的本质说起来并不复杂:访问顺序冲突。
- 事务A:先锁订单 → 再锁库存
- 事务B:先锁库存 → 再锁订单
→ 结果就是瞬间死锁。
解决方案也很直接:在业务代码层,所有表和行的访问顺序必须统一约定。比如所有人一律“先操作库存,再操作订单”,永不例外。
3. 给查询条件加索引
没有索引的情况下,行锁会升级成表锁,死锁的概率会暴增好几个数量级。
-- 错误:无索引,会锁全表UPDATE user SET money=100 WHERE name='张三'; -- 正确:name 有索引,只锁单行UPDATE user SET money=100 WHERE name='张三';
如果查看死锁日志时发现里面有 LOCK_MODE: X, REC_NOT_GAP: nil,基本可以断定就是缺少索引引起的。
4. 业务代码捕获死锁异常,自动重试
死锁说到底是一个偶发异常,重试一次就能解决,这是整个方案里最稳妥的兜底措施。
以Java为例:
try { // 执行数据库操作} catch (SQLTransactionRollbackException e) { if (e.getMessage().contains("Deadlock")) { // 死锁异常,重试1-2次 retry(); }}
三、快速排查死锁 Checklist
- 执行
SHOW ENGINE INNODB STATUS;查看死锁详情 - 检查两个事务是否访问表或行的顺序相反
- 检查更新语句是否缺少索引
- 检查是否存在长事务
- 业务代码是否没有实现重试机制
总结
- 已产生的死锁:MySQL会自动回滚一个事务,无需人工介入
- 查找死锁原因:使用
SHOW ENGINE INNODB STATUS;命令 - 根治死锁:固定访问顺序 + 合理加索引 + 短事务 + 代码重试
- 兜底方案:业务层捕获死锁异常并自动重试,用户几乎无感知

