MySQL死锁可通过错误码1213/1205识别,再结合SHOW ENGINE INNODB STATUS\G查看两个冲突事务的SQL、索引、锁类型及等待关系,并通过EXPLAIN和隔离级别分析根因,最后以重试、拆事务、统一加锁顺序和优化索引规避。
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 块即为对峙双方一份典型死锁日志包含三类核心信息,逐块对照着看:
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)引发的隐式锁定。
从日志里提取出两条关键UPDATE/DELETE语句后,下一步要验证它们是否真的会触发锁冲突:
EXPLAIN 看执行计划,没走索引容易升级为表级扫描+大量行锁WHERE id = ?)只锁匹配行;非唯一索引或范围查询(WHERE status IN (1,2))可能触发next-key lock(行锁+间隙锁),扩大锁定范围SELECT @@tx_isolation;,可重复读(REPEATABLE-READ)下间隙锁更活跃,比读已提交(READ-COMMITTED)更容易死锁线上已报死锁,先稳住服务:
SET SESSION innodb_lock_wait_timeout = 60;(避免长时间挂起,但不解决根本问题)product_id ASC更新库存)、给高频WHERE字段补上覆盖索引死锁
