INSERT IGNORE 更易死锁是因为冲突时仍持插入意向锁,与唯一索引锁冲突;ON DUPLICATE KEY UPDATE 需避免空更新;根治方案是先 SELECT ... FOR UPDATE 显式加锁再操作。
它不加锁就插入,看起来很轻量,但实际在冲突时会触发隐式锁行为:InnoDB 先尝试插入,发现 ERROR 1062 后回退,过程中仍会持有插入意向锁(Insert Intention Lock),而这个锁和唯一索引上的 S/X 锁存在兼容性冲突。多个事务并发执行 INSERT IGNORE 到同一唯一键范围(比如手机号、订单号),极易因锁等待顺序错乱形成循环依赖。
常见错误现象:Deadlock found when trying to get lock 报错频繁,且往往集中在某个热点唯一键值附近(如 uk_value = 12345)。
SELECT ... FOR UPDATE 提前占位,因为 INSERT IGNORE 不触发一致性读也不阻塞其他事务查该键它比 INSERT IGNORE 更可控,但默认行为有陷阱:如果 UPDATE 子句中所有字段值与当前行完全一致(比如只写 updated_at = NOW(),但时间精度相同导致无实际变更),InnoDB 可能跳过行锁优化,导致后续语句仍竞争同一间隙或记录锁。
实操建议:
updated_at = updated_at + 0 或 version = version + 1
UPDATE,例如 ON DUPLICATE KEY UPDATE id = id 在某些版本下等价于无操作,锁行为异常REPLACE INTO 替代,它本质是 DELETE + INSERT,会释放原行锁再申请新锁,放大死锁窗口靠 SQL 语法绕不开锁竞争,真正降低死锁概率的方式是把“判断是否存在”提前到事务最开始,并用确定性方式锁定目标行。
典型流程:
SELECT id FROM t WHERE uk_value = ? FOR UPDATE
UPDATE;若没查到,走 INSERT
uk_value,再操作其他字段)FOR UPDATE 查询后夹杂非数据库操作(如 HTTP 调用、日志写入),否则锁持有时间拉长,冲突概率上升注意:这个方案要求唯一键字段上有有效索引,否则 FOR UPDATE 会升级为表锁或大量间隙锁,引发更严重问题。
应用日志里看到 ERROR 1213 (40001) 只是结果,真正要定位的是哪两个事务、在哪条索引、以什么顺序加了哪些锁。
关键动作:
SHOW ENGINE INNODB STATUSG,找 LATEST DETECTED DEADLOCK 块WAITING FOR THIS LOCK TO BE GRANTED 和 HOLDS THE LOCK(S) 对应的索引名(如 uk_value)、记录值(如 hex 6162632d3133302d737a 解码后是 abc-130-sz)page no 相同)上争抢相邻记录,这是典型唯一索引热点冲突最容易被忽略的一点:死锁日志里的 heap no 是页内偏移,不是主键值;同一个唯一键冲突可能分散在不同数据页,也可能挤在一页里——后者死锁密度更高,需优先拆分写入粒度。
Tplink企业版路由器WiFi名称的默认设置介绍(Tplink企业版路由器WiFi名称的默认设置是什么)
Tplink路由器灯常亮无法上网的原因分析(如何解决Tplink路由器灯常亮无法上网的问题)
Tplink千兆企业级路由器自动重启的作用和优势介绍(如何设置Tplink千兆企业级路由器自动重启功能)
一根天线的tplink路由器有哪些(一根天线的Tplink路由器的特点和优势介绍)
tplink路由器外网访问不了nas(Tplink路由器外网访问NAS的原因分析)
Tplink无法搜到路由器的原因分析(如何解决Tplink无法搜到路由器的问题)