MySQL死锁可通过错误码1213/1205识别,再结合SHOW ENGINE INNODB STATUS\G查看两个冲突事务的SQL、索引、锁类型及等待关系,并通过EXPLAIN和隔离级别分析根因,最后以重试、拆事务、统一加锁顺序和优化索引规避。

怎么确认发生了MySQL死锁
MySQL发生死锁时,InnoDB会自动检测并终止其中一个事务,返回错误码 1213(Deadlock found when trying to get lock)或 1205(Deadlock found; try restarting transaction)。应用层看到这类报错,基本可判定存在死锁。
但仅靠报错不够——你需要确认是哪两个事务、哪几行数据、什么SQL引发了冲突。关键手段有两个:
- 开启全局死锁日志:
SET GLOBAL innodb_print_all_deadlocks = ON;,之后所有死锁都会记录到MySQL错误日志(如/usr/local/mysql/data/mysqld.local.err) - 查最近一次死锁快照:
SHOW ENGINE INNODB STATUS\G,输出中*** (1) TRANSACTION和*** (2) TRANSACTION块即为对峙双方
死锁日志里看什么
一份典型死锁日志包含三类核心信息,逐块对照着看:
-
事务基本信息:事务ID(
TRANSACTION xxx)、活跃时间(ACTIVE X sec)、线程ID(MySQL thread id)、执行的SQL(query id xxx ... updating/inserting...) -
持有的锁:标记为
HOLDS THE LOCK(S),说明该事务已成功加锁的索引页、行位置、锁模式(如lock_mode X locks rec but not gap表示对某行加了排他行锁) -
等待的锁:标记为
WAITING FOR THIS LOCK TO BE GRANTED,说明它卡在哪儿——通常正等着对方持有的那把锁释放
重点比对两个事务的 index 名称、space id 和 page no:如果完全一致,说明它们在争同一张表的同一页甚至同一行;若索引不同,可能是联合索引覆盖不全或间隙锁(gap lock)引发的隐式锁定。
快速定位冲突SQL和表结构
从日志里提取出两条关键UPDATE/DELETE语句后,下一步要验证它们是否真的会触发锁冲突:
- 检查WHERE条件是否命中索引:用
EXPLAIN看执行计划,没走索引容易升级为表级扫描+大量行锁 - 确认索引类型:唯一索引上的等值查询(
WHERE id = ?)只锁匹配行;非唯一索引或范围查询(WHERE status IN (1,2))可能触发next-key lock(行锁+间隙锁),扩大锁定范围 - 查表当前隔离级别:
SELECT @@tx_isolation;,可重复读(REPEATABLE-READ)下间隙锁更活跃,比读已提交(READ-COMMITTED)更容易死锁
当场处理与后续规避
线上已报死锁,先稳住服务:
- 应用层捕获1213/1205错误后,立即重试事务(通常1–2次即可成功)
- 若频繁死锁影响业务,临时可调大锁等待超时:
SET SESSION innodb_lock_wait_timeout = 60;(避免长时间挂起,但不解决根本问题) - 长期方案聚焦三点:拆小事务、统一加锁顺序(如所有服务按
product_id ASC更新库存)、给高频WHERE字段补上覆盖索引
死锁不是故障,是并发系统的正常现象。能被自动发现并回滚,恰恰说明InnoDB工作正常。真正要警惕的,是日志里反复出现相同SQL组合的死锁——那往往意味着代码逻辑或索引设计有硬伤。










