mysql死锁发生后如何处理_mysql恢复机制说明

来源:这里教程网 时间:2026-02-28 20:43:50 作者:

死锁发生后,MySQL 会自动选一个事务回滚,另一个继续执行——你不用手动干预,但必须在应用层捕获错误并重试。

遇到
ERROR 1213 (40001): Deadlock found when trying to get lock
怎么办

这不是数据库挂了,而是 InnoDB 正常的死锁检测机制在起作用:它发现循环等待后,主动杀死(回滚)其中一个事务,让另一个顺利提交。关键是你得让业务代码“接住”这个错误。

必须在应用代码里捕获
1213
错误码,而不是当作普通异常吞掉或直接报 500
推荐重试 1–2 次(带指数退避),比如延迟 10ms 后重跑整个事务逻辑 不要盲目重试所有 SQL;只对明确是“更新/插入竞争同一行”的业务逻辑加重试(如库存扣减、订单状态变更) 如果重试后还报 1213,大概率是设计问题(比如锁顺序不一致),不是偶发现象

SHOW ENGINE INNODB STATUS
能看到什么、怎么看

这条命令返回的是最近一次死锁的完整现场快照,重点看

LATEST DETECTED DEADLOCK
下方内容。它告诉你谁等谁、锁了哪些行、执行了什么 SQL。

搜索
*** (1) TRANSACTION
和
*** (2) TRANSACTION
—— 分别对应两个冲突事务
关注每个事务里的
mysql tables in use
、
locked tables
和
LOCK WAIT
行
最关键的是
WAITING FOR THIS LOCK TO BE GRANTED
和
HOLDS THE LOCK(S)
两行,能还原出“谁持有了什么、又在等什么”
注意
Trx id
和
OS thread id
可用来关联慢日志或 binlog,但命令本身不保留历史,只存最后一次
SHOW ENGINE INNODB STATUS\G

如何让 MySQL 记录每次死锁(不只是最后一次)

默认只留最近一次,线上排查必须开启全量记录,否则错过就没了。

设置全局参数:
SET GLOBAL innodb_print_all_deadlocks = ON
(需 SUPER 权限)
该参数生效后,每次死锁都会写入 MySQL 错误日志(
error.log
),格式清晰可 grep
注意:日志量会增加,但远小于慢查询日志,生产环境建议始终开启 不需要重启 MySQL,但该参数不会持久化,记得写进
my.cnf
的
[mysqld]
段落:
innodb_print_all_deadlocks = ON

为什么不能靠
ROLLBACK
或重启解决死锁

手动

ROLLBACK
对已发生的死锁无效——InnoDB 在报错前已经完成回滚;而重启 MySQL 更是危险操作:它会清空所有事务上下文,但无法修复业务逻辑缺陷,还会导致主从延迟、连接闪断、监控报警风暴。

死锁不是故障,是并发控制的正常结果;处理目标是“让业务稳”,不是“让日志静” 把重试逻辑写死在 DAO 层或 service 方法里,比靠 DBA 人工介入更可靠 真正要动配置的,只有
innodb_lock_wait_timeout
(超时回滚)和
innodb_deadlock_detect
(默认 ON,关了反而更容易卡住)
最隐蔽的坑:用 ORM 自动生成 SQL 时,不同接口可能以不同顺序 update 多张表(比如先改订单再改库存, vs 先改库存再改订单),这种不一致才是死锁温床

死锁日志里那几行 SQL 看着简单,但背后往往是多个模块耦合修改同一组数据——查日志只是起点,归因要落到代码调用链和资源访问顺序上。

相关推荐