mysql事务死锁是多个事务以不一致顺序争抢行锁或间隙锁导致的循环等待结果;innodb自动回滚undo量小的事务,但高频发生表明加锁逻辑或索引设计存在缺陷。

MySQL事务死锁不是偶发异常,而是多个事务以不一致顺序争抢行锁或间隙锁时必然出现的循环等待结果;InnoDB会自动回滚undo量小的事务,但频繁发生说明加锁逻辑或索引设计存在硬伤。
死锁最常发生的场景:多表/多行更新顺序不一致
两个事务操作相同资源(比如user和order表),但加锁顺序相反:事务A先锁user.id=1再锁order.order_id=100,事务B反过来先锁order.order_id=100再锁user.id=1。只要并发稍高,立刻形成循环等待。
- 这是生产环境占比超60%的死锁原因
- 批量更新单表多行时,
WHERE id IN (...)中的ID必须提前ORDER BY id排序,否则InnoDB内部加锁顺序不可控(即使IN列表乱序,InnoDB也按主键升序加锁,但你不该依赖这个行为) - ORM如GORM、MyBatis若未显式控制
WHERE条件字段顺序,极易因动态SQL生成顺序不同引发隐式死锁
快速定位死锁:只用一条命令看懂现场
应用报错ERROR 1213 (40001): Deadlock found when trying to get lock后,第一时间执行:
SHOW ENGINE INNODB STATUS\G
重点关注输出中的LATEST DETECTED DEADLOCK部分,它会清晰展示:
- 发生死锁的两个事务及其完整
SQL - 各自持有的锁(
HOLDS THE LOCK(S))和等待的锁(WAITING FOR THIS LOCK TO BE GRANTED) - 被回滚的事务(
WE ROLL BACK TRANSACTION)
注意:该命令只返回最近一次死锁,如需长期记录所有死锁,需在my.cnf中配置innodb_print_all_deadlocks = 1并确保log_error路径可写。
容易被忽略的关键点:索引缺失会让死锁从“偶发”变“高频”
当WHERE条件没走索引,InnoDB会逐条扫描聚簇索引并加行锁,相当于对整张表加了大量行锁。此时哪怕只是两个简单UPDATE,也可能因锁住不同行却互相等待而死锁。
- 常见错误现象:
EXPLAIN显示type = ALL或key = NULL;慢查询日志里Rows_examined远大于实际匹配行数;死锁日志中锁类型为X但锁定范围是成百上千行 - 高频
WHERE字段必须建索引,联合索引注意最左前缀原则(例如WHERE status = ? AND created_at > ?,应建(status, created_at)而非(created_at, status)) - 禁止在索引字段上做函数操作:
WHERE DATE(create_time) = '2026-07-01'会跳过索引,应改用WHERE create_time >= '2026-07-01' AND create_time











