MySQL已产生死锁的解决方法及永久避免方案
在MySQL数据库中,死锁的发生可以用一个场景来理解:多个事务相互争夺资源,互不相让,最终导致全部阻塞。MySQL内置的死锁检测机制能够迅速发现这种局面,并主动选择一个代价最小的事务进行回滚,同时抛出死锁异常,使另一个事务得以继续执行。这正是我们经常看到“Deadlock found when tr
在MySQL数据库中,死锁的发生可以用一个场景来理解:多个事务相互争夺资源,互不相让,最终导致全部阻塞。MySQL内置的死锁检测机制能够迅速发现这种局面,并主动选择一个代价最小的事务进行回滚,同时抛出死锁异常,使另一个事务得以继续执行。这正是我们经常看到“Deadlock found when trying to get lock”错误的原因。
本文主要解决两大实际问题:
- 死锁发生后如何处理:业务被死锁卡住时,如何快速恢复?
- 从根源上预防死锁:如何避免此类“堵车”反复出现?

一、已产生死锁:三步快速解决
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;
二、永久避免死锁:四个核心方案(必做)
从技术层面讲,死锁无法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; - 根治死锁:固定访问顺序 + 加索引 + 短事务 + 代码重试
- 兜底:业务层捕获死锁异常并自动重试,用户几乎无感知
