Deadlock found when trying to get lock; try restarting transaction是InnoDB检测到循环等待后主动回滚代价小的事务所致,根本原因是空表/低数据量表在REPEATABLE READ下触发Next-Key Lock,所有INSERT竞争(-∞, +∞)间隙导致Insert Intention Lock排队混乱,需通过ON DUPLICATE KEY UPDATE、降级READ COMMITTED或统一加锁顺序解决。

Deadlock found when trying to get lock; try restarting transaction 这个错误不是“插入失败”,而是 InnoDB 明确告诉你:两个或多个事务在抢同一片空隙,锁住彼此了。
根本原因不在你写了几条 INSERT,而在空表或低数据量表 + 默认 REPEATABLE READ 隔离级别 + 并发写入这三者叠加——它会触发 Next-Key Lock(记录锁 + 间隙锁),而空表的主键/唯一索引间隙是 (-∞, +∞),所有并发 INSERT 都试图在这个超大间隙里加 Insert Intention Lock。这个意向锁本该兼容,但因无实际记录锚定,InnoDB 锁队列机制容易形成循环等待链,最终必死锁。
为什么空表 INSERT 必然高概率死锁
这不是偶发问题,是 InnoDB 在 REPEATABLE READ 下的确定性行为:
- 空表没有主键值,InnoDB 只能对整个索引范围加间隙锁,所有
INSERT都落在同一个(-∞, +∞)间隙上 -
Insert Intention Lock虽兼容,但需排队申请;无行可锚定,锁等待顺序易乱,检测到循环后立即回滚一个事务 - 即使只有一列
id INT PRIMARY KEY AUTO_INCREMENT,只要没数据,就逃不掉 - 二级唯一索引(如
email)上并发INSERT同样触发,且锁范围更大(先锁二级索引,再锁主键)
ON DUPLICATE KEY UPDATE 是最稳的替代方案
它原子执行、不依赖应用层 SELECT 判断,且只锁命中行及其间隙,不会扩散。
MySQL 数据库管理技能,能够通过自然语言查询和管理数据库,支持 SELECT/INSERT/UPDATE/DELETE、表管理、备份恢复等操作。当用户提及 MySQL、数据库查询、建表或数据备份时使用此技能。
- 前提:目标字段必须有
UNIQUE KEY或PRIMARY KEY(比如order_no加唯一索引) - 哪怕只是想“忽略重复”,也别写空
UPDATE,否则 InnoDB 可能跳过锁优化:INSERT INTO t (a,b) VALUES (1,2) ON DUPLICATE KEY UPDATE b = VALUES(b) -
INSERT IGNORE和REPLACE INTO不推荐:前者仍要申请冲突行的 S 锁,后者本质是“删 + 插”,锁更重、耗时更长,反而加剧死锁
降级到 READ COMMITTED 能根治空表死锁
这是从机制上禁用间隙锁的最直接方式,代价清晰可评估。
- 会话级生效:
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED - 全局配置(需重启):
transaction_isolation = 'READ-COMMITTED'写入my.cnf - 副作用只有幻读:事务 A 查不到某记录,B 插入并提交后,A 再查就能看到——对幂等写入(如订单创建、日志落库)通常无实质影响
- 注意:如果业务强依赖 RR 级别的可重复读语义(如财务对账),不能盲目降级
真正容易被忽略的点是:**死锁日志里报的那条 SQL 往往不是真凶,而是最后被卡住的那一个**。它前面可能已持有了其他锁,而另一事务正等着它释放——所以光看报错 SQL 没用,必须结合 SHOW ENGINE INNODB STATUS\G 里的 HOLDS THE LOCK(S) 和 WAITING FOR THIS LOCK TO BE GRANTED 两块交叉比对,才能定位到循环等待的真实断点。










