REPLACE INTO 不是避免主键冲突异常,而是将冲突转化为 DELETE + INSERT;当表有多个唯一索引且新数据分别匹配不同旧记录时会删除多行;主从 auto_increment 不一致源于主库分配新 ID 而从库 binlog 回放可能忽略更新;应优先使用 ON DUPLICATE KEY UPDATE 实现安全更新。

REPLACE INTO 不是用来“避免主键冲突异常”的
它根本不会避免异常,而是把“主键冲突”当作一个触发条件,主动执行 DELETE + INSERT。你看到的“没报错”,只是因为 MySQL 把冲突转化成了删除再插入的动作——但这不等于安全或正确。真正想避免异常并保留数据一致性,应该用 INSERT ... ON DUPLICATE KEY UPDATE。
REPLACE INTO 在什么情况下会删多行?
当表有多个唯一索引(比如 UNIQUE KEY email 和 UNIQUE KEY phone),而新数据恰好分别撞上不同旧记录时,MySQL 会删掉所有匹配的行。这不是 bug,是文档明确写的行为:
- 插入
(email='a@b.com', phone='123'),但已有记录 A 的email='a@b.com'、记录 B 的phone='123' -
REPLACE INTO会同时删除 A 和 B,再插入新行 - 结果是两条旧数据全丢,且自增 ID 跳两次
为什么 REPLACE INTO 会导致主从 auto_increment 不一致?
主库执行 REPLACE INTO 时,自增 ID 会分配一次(DELETE 后 INSERT 新行);但从库回放 binlog 时,可能只记录为一条 UPDATE 事件(尤其在某些 MySQL 5.7 配置下),导致从库的 auto_increment 值没更新。一旦切换为主库,后续 INSERT 就会撞上重复 ID。
这个问题在高可用切换场景中非常隐蔽:业务日志里看不到错误,直到某次写入突然报 ERROR 1062 (23000): Duplicate entry '12345' for key 'PRIMARY'。
真正该用什么代替 REPLACE INTO?
绝大多数“存在则更新”需求,都应该用 ON DUPLICATE KEY UPDATE:
- 它不删行,所以自增 ID 不变、外键不断、触发器不误触
- 支持表达式,比如
counter = counter + 1或updated_at = NOW() - 影响行数可区分:返回 1 是插入,2 是更新,0 是被
IGNORE(如果加了) - 并发下更可控——哪怕两个线程同时更新同一行,只要字段不互相覆盖,就不会丢数据
唯一要注意的是:必须确保表上有 PRIMARY KEY 或 UNIQUE KEY,否则 ON DUPLICATE KEY UPDATE 不生效。


















