直接查看LATEST DETECTED DEADLOCK可定位死锁原因:(1) TRANSACTION和(2) TRANSACTION显示冲突事务的线程ID与SQL;(1) HOLDS THE LOCK(S)与(2) WAITING FOR THIS LOCK TO BE GRANTED揭示持锁与等待锁关系;间隙锁多因RR级范围查询无索引,行锁冲突常因多表更新顺序不一;WHERE未走索引会引发全表扫描加锁;INSERT ... ON DUPLICATE KEY UPDATE在并发下易死锁;隐式事务或Sleep连接长期持锁亦是常见诱因。

直接看 LATEST DETECTED DEADLOCK 输出,它已经告诉你谁被回滚、哪条 SQL、锁了什么、为什么死锁——别绕路查日志或 processlist。
怎么看 LATEST DETECTED DEADLOCK 的关键字段
执行 SHOW ENGINE INNODB STATUS\G 后,找到 LATEST DETECTED DEADLOCK 区块。重点盯这三块:
-
*** (1) TRANSACTION:和*** (2) TRANSACTION:—— 分别是两个冲突事务的快照,注意trx_mysql_thread_id(即线程 ID)和trx_query(正在执行的 SQL) -
*** (1) HOLDS THE LOCK(S):—— 这个事务当前持有哪些锁(比如X locks rec but not gap行锁,或X locks gap before rec间隙锁) -
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:—— 它正卡在等什么锁,而这个锁恰好被事务 (1) 持有
如果看到 gap before rec 或 next-key lock,基本可断定是 RR 隔离级别下因范围查询 + 无索引/索引失效引发的间隙锁交叉;如果全是 rec but not gap,更可能是多表更新顺序不一致导致。
WHERE 条件没走索引时死锁概率飙升
当 UPDATE 或 DELETE 的 WHERE 条件无法命中索引,InnoDB 会退化为全表扫描并加大量行锁(甚至接近表锁),极大增加与其他事务锁范围重叠的概率。
- 用
EXPLAIN确认你的语句是否走了预期索引,尤其注意type是range还是ALL,key是否为空 - 联合索引要满足最左前缀,
WHERE a = ? AND b > ?能用上(a,b),但WHERE b > ?就用不上 -
ORDER BY+LIMIT类语句若没索引,也可能触发非预期锁范围扩展
例如:对没有索引的 status 字段执行 UPDATE orders SET paid = 1 WHERE status = 'unpaid',可能锁住成百上千行,而另一个事务刚好在更新其中某几行——死锁就来了。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
事务里混用 INSERT ... ON DUPLICATE KEY UPDATE 很危险
这个语句在并发场景下极易触发死锁,官方 Bug #58637 明确记录过:它会在唯一键冲突路径上同时申请插入意向锁(insert intention lock)和记录锁(record lock),而不同事务的加锁顺序可能不一致。
- 不要在高并发写入路径中依赖它做“存在则更新、不存在则插入”的原子操作
- 替代方案:先
SELECT ... FOR UPDATE判断是否存在,再明确走INSERT或UPDATE,确保加锁顺序可控 - 如果必须用,确保该唯一键字段有有效索引,且事务中无其他非必要锁操作
典型报错就是 Deadlock found when trying to get lock; try restarting transaction,但错误日志里看不到具体 SQL,必须靠 LATEST DETECTED DEADLOCK 定位到那条 INSERT ... ON DUPLICATE KEY UPDATE。
别忽略隐式事务和未提交的 sleep 连接
很多死锁不是由业务 SQL 直接引起,而是某个连接长时间处于 Sleep 状态却仍持有事务锁——比如 ORM 开启了自动事务但异常分支没 ROLLBACK,或者应用层忘了 commit。
- 查
information_schema.INNODB_TRX,重点关注trx_state = 'RUNNING'且trx_started时间异常久(比如 > 30 秒)的记录 - 关联
information_schema.PROCESSLIST查对应id的COMMAND是否为Sleep,TIME是否持续增长 - 这类连接一旦遇到 DDL(如
ALTER TABLE),还会引发Waiting for table metadata lock,连锁阻塞更多请求
真正棘手的是:这种连接本身不报错、不打日志,但会让后续所有想访问同一张表的事务排队等待,直到超时或被 kill。排查时最容易漏掉它。

















