REPLACE INTO会加两次X锁,因其执行“先试插(对UK加X锁)→冲突回滚→再检索冲突行并重加X锁(为DELETE+INSERT或UPDATE准备)”两阶段加锁;高并发下因insert intention lock与next-key lock互相等待而易死锁。

REPLACE INTO 的锁开销大,根本原因在于它不是原子的“更新”操作,而是“先试插、再删再插”或“先试插、再更新”的两阶段加锁流程,且在冲突路径上会叠加多种锁类型。高并发下极易形成锁等待链甚至死锁。
REPLACE INTO 为什么会加两次 X 锁?
当唯一键(UK)冲突发生时,REPLACE INTO 实际执行了两个逻辑阶段,每个阶段都独立加锁:
- 第一阶段:尝试
INSERT,发现 UK 冲突后回滚聚集索引插入,但**二级唯一索引上已加了LOCK_X记录锁(不是LOCK_S)** —— 这是和普通INSERT最关键的区别 - 第二阶段:Server 层收到
DB_DUPLICATE_KEY后,检索冲突行,对**该 UK 记录 + 对应的主键记录,再次加LOCK_X(或 next-key lock)**,为后续 DELETE + INSERT 或 UPDATE 做准备 - 如果走 DELETE + INSERT 路径(绝大多数情况),还会触发对旧记录的标记删除(需加 X 锁)、新记录插入(需加 insert intention lock + gap lock)
也就是说,一次冲突的 REPLACE INTO 可能持有 2~3 把 X 级别锁,且锁粒度跨聚集索引和二级索引。
为什么并发 REPLACE INTO 容易触发死锁?
死锁核心来自 insert intention lock 和 next-key lock 的互相等待,典型场景如下:
- Session A 执行
REPLACE INTO t(b) VALUES (8),在唯一索引b=8上加了LOCK_Xnext-key lock,并回滚了初始插入 - Session B 同时执行相同语句,被 A 的 next-key lock 阻塞,开始等待该锁释放
- 但 Session A 在进入第二阶段时,又需要获取
insert intention lock来重插(哪怕 PK 自增不同),而该意向锁又与 B 持有的锁范围重叠 - 结果:A 等 B 释放 next-key lock,B 等 A 释放 insert intention lock → 死锁
MySQL 5.7+ 的死锁日志里常看到类似:waiting for this lock to be granted: ... insert intention waiting 和 holds the lock: ... next-key X lock —— 这不是误报,而是真实锁依赖链。
REPLACE INTO vs INSERT ... ON DUPLICATE KEY UPDATE 的锁行为差异
二者表面功能相似,但底层加锁逻辑完全不同:
-
INSERT ... ON DUPLICATE KEY UPDATE在检测到 UK 冲突时,**只对冲突行加一把LOCK_X(或 next-key lock),然后直接原地 UPDATE**,不回滚、不删行、不重插 -
REPLACE INTO即使最终走 UPDATE 路径(极少数:最后一个 UK + 无外键 + 无 DELETE trigger),也**仍会先加一次插入锁、再加一次更新锁**,中间还涉及 undo 回滚开销 - 性能实测中,在 UK 冲突率 > 10% 的场景下,
ON DUPLICATE KEY UPDATE的吞吐量通常是REPLACE INTO的 2~5 倍,锁等待减少 70% 以上
注意:ON DUPLICATE KEY UPDATE 不支持对主键列本身赋新值(比如想改 id),而 REPLACE INTO 可以 —— 这是它唯一不可替代的场景,但也正是这个能力带来了额外锁和 purge 压力。
自增 ID 和锁顺序不可控加剧并发风险
REPLACE INTO 中若未显式指定自增主键值,InnoDB 会分配一个新值,但:
- 分配过程受
innodb_autoinc_lock_mode控制,默认为 1(interleaved),意味着多个并发REPLACE的自增值**不保证插入顺序与事务开始顺序一致** - 这导致事务之间无法预测谁先拿到哪个 gap 区间,进一步放大 insert intention lock 与 gap lock 的碰撞概率
- 即使表里只有一条
b=8的记录,高并发REPLACE INTO ... VALUES (8)仍可能因自增分配时机错乱,在不同 page 上产生竞争
真正难调试的点就在这里:现象随机、复现靠压测脚本、死锁日志里看不出明显“谁先谁后”。











