MySQL死锁错误表现为ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction,是多个事务互持对方所需锁导致的阻塞,MySQL主动回滚代价小的事务以解除死锁。

MySQL死锁错误长什么样?
你执行 UPDATE 或 DELETE 时突然收到类似这样的报错,就是典型的死锁:
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
这不是数据库挂了,也不是语法错了,而是两个或多个事务互相持有对方需要的锁,谁也不肯先放——卡死了。MySQL 检测到后主动杀掉其中一个事务(通常是代价小的那个),让它重试。
什么操作容易触发死锁?
死锁不是随机发生的,常见于以下场景:
- 多个事务以不同顺序更新同一组行,比如事务 A 先更新
id=1再更新id=2,事务 B 反过来先更新id=2再更新id=1 - 在非唯一索引上做范围更新(如
WHERE status = 'pending'),导致锁住不止一行,甚至间隙锁(gap lock)参与进来 - 事务中混用
SELECT ... FOR UPDATE和普通 DML,且读写顺序不一致 - 长时间未提交的事务(比如在应用层做了大量计算或调用了外部 API),让锁持有时间变长,冲突概率上升
怎么查死锁原因?
别只看报错,MySQL 记录了每次死锁的详细信息,用这条命令就能看到最近一次死锁的完整分析:
SHOW ENGINE INNODB STATUS\G
重点看输出里 LATEST DETECTED DEADLOCK 这一节,它会告诉你:
- 哪个事务被回滚(
ROLLING BACK) - 每个事务正在等待什么锁(
waiting for this lock to be granted) - 每个事务已经持有哪些锁(
holds the locks) - 涉及的表、索引、具体记录(
record lock on index ... space id ... heap no ...)
注意:这个日志只保留最后一次死锁,排查时要快;生产环境建议开启 innodb_print_all_deadlocks = ON,把所有死锁写进 error log。
怎么避免和缓解死锁?
根本思路是减少锁冲突窗口 + 统一访问顺序:
- 按主键或唯一索引顺序更新数据,比如批量处理前先
ORDER BY id排序再遍历 - 尽量缩短事务长度:DML 后尽早
COMMIT,别在事务里做耗时操作 - 避免在事务中使用
SELECT ... FOR UPDATE读取非必要数据;如果只是校验,考虑用一致性读(普通SELECT)+ 应用层重试逻辑 - 对高频更新的热点行,可考虑拆分(如把一个计数器分散成多行)或改用乐观锁(版本号字段 +
WHERE version = ?) - 应用层必须捕获
ERROR 1213并自动重试(但要限制重试次数,防止雪崩)
死锁无法完全消除,但高频死锁往往暴露的是业务逻辑缺陷或 SQL 设计问题,而不是配置调优能解决的。










