Deadlock found when trying to get lock 是 MySQL 检测到事务间循环等待锁而主动回滚牺牲事务的正常现象;需通过 SHOW ENGINE INNODB STATUS 查 LATEST DETECTED DEADLOCK 确认,应用层须捕获错误码 1213 并重试。

MySQL 报错 Deadlock found when trying to get lock 是什么情况
这不是数据库坏了,而是两个或多个事务互相持有对方需要的锁,又都在等对方释放,卡住了。MySQL 检测到后会主动杀掉其中一个事务(通常是代价小的那个),让它报这个错,另一个继续执行。
关键点:死锁是正常现象,不能完全避免,但可以大幅降低发生概率;报错的是被选为“牺牲者”的那个事务,它需要重试。
怎么复现和确认是不是真死锁
别靠猜。先查最近的死锁日志:
SHOW ENGINE INNODB STATUS\G
重点看 LATEST DETECTED DEADLOCK 区域,里面会明确写出两个事务各自执行了哪条 SQL、持有哪些锁、等待什么锁——这是唯一可信的依据。
常见误判场景:
- 误把
Lock wait timeout exceeded当成死锁(其实是单个事务等锁超时,没检测到循环等待) - 应用层没捕获
Deadlock found when trying to get lock错误码(MySQL 返回码是1213),导致重试逻辑缺失 - 事务里混用了
SELECT ... FOR UPDATE和普通SELECT,但没加WHERE条件,导致锁住整张表
减少死锁的核心操作顺序规则
绝大多数可预防的死锁,根源是事务以不同顺序访问相同资源。解决思路很直接:让所有事务按同一顺序访问行、索引、表。
实操建议:
- UPDATE/DELETE 语句必须走索引,且尽量用主键或唯一索引;避免
WHERE status = ?这类无索引扫描,否则可能锁住大量无关行 - 批量更新时,先
SELECT id FROM ... ORDER BY id拿到有序主键列表,再按顺序逐条UPDATE,不要用IN (unsorted_ids) - 同一个业务逻辑里,如果要更新多张表,固定先后顺序(比如总是先
UPDATE orders再UPDATE order_items) - 事务粒度尽量短,避免在事务里做 HTTP 调用、文件读写等耗时操作
应用层必须做的错误处理
MySQL 主动抛出死锁后,事务已回滚,连接仍可用。不处理就等于丢数据。
正确做法:
- 捕获数据库异常中
errno == 1213或错误信息含Deadlock found when trying to get lock - 最多重试 2–3 次,每次加随机毫秒级延迟(防雪崩),超过则放弃并记录告警
- 避免在重试逻辑里重新生成业务 ID 或调用幂等性弱的外部接口(比如重复扣款)
- 如果是高并发写同一行(如库存扣减),考虑改用
UPDATE table SET stock = stock - 1 WHERE id = ? AND stock >= 1+ 影响行数判断,比事务更轻量
真正麻烦的不是死锁本身,而是事务里夹杂了非数据库操作、或者重试时没校验业务前置条件是否已变——这些不会出现在 SHOW ENGINE INNODB STATUS 里,但会让重试变成灾难。










