replace into 本质是 delete + insert,非 update;先删旧行再插新行,导致自增 id 变化、触发器执行两次、外键短暂中断,且无法保留原字段值。

REPLACE INTO 本质是 DELETE + INSERT,不是 UPDATE
它看起来像“更新”,但底层会先尝试根据主键或唯一索引匹配行;若存在,则删掉旧行再插入新行;若不存在,就直接插入。这意味着自增 ID 会变化、触发器会执行两次(DELETE 和 INSERT 各一次)、外键约束可能被短暂中断。
- 如果表有
AUTO_INCREMENT字段,REPLACE INTO成功后该值大概率会递增,哪怕只是“逻辑上更新”同一行 -
ON DELETE CASCADE关联的子记录会被真正删除,再随新主记录重建(如果子表也用 REPLACE) - 没有
ON DUPLICATE KEY UPDATE那种“原地改字段”的语义,别指望它能避免主键变更带来的副作用
什么情况下必须用 REPLACE INTO 而非 INSERT ... ON DUPLICATE KEY UPDATE
只有当你明确需要“原子性地替换整行”,且能接受 DELETE + INSERT 的全部行为时才考虑它。典型场景极少,比如:缓存表全量刷新、幂等写入要求严格覆盖(而非合并字段)、或某些分库分表中间件只支持 REPLACE 语法。
- 想保留原
created_at时间戳?别用REPLACE INTO,它会重置所有字段,包括未显式指定的默认值列 - 想只更新部分字段(如仅改
status),应该用INSERT ... ON DUPLICATE KEY UPDATE status = VALUES(status) - 某些老版本 MySQL(ON DUPLICATE KEY UPDATE 的表达式支持有限,这时
REPLACE INTO可能是权宜之计,但得清楚代价
REPLACE INTO 报错 “Duplicate entry” 却没生效?
这不是 bug,是它在找唯一冲突时失败了——说明你没定义主键或唯一索引,或者定义了但字段组合不匹配实际数据。MySQL 只有在遇到 Duplicate entry 'xxx' for key 'yyy' 这类错误时才会触发替换逻辑。
- 检查表结构:
SHOW CREATE TABLE table_name,确认有PRIMARY KEY或UNIQUE KEY - 确保
REPLACE INTO语句里包含能触发冲突的字段值,比如唯一索引是(a, b),你就得同时提供a和b - 注意 NULL 处理:多个 NULL 值在唯一索引中不视为重复,所以
(1, NULL)和(1, NULL)可能不会触发 REPLACE
性能与复制安全问题常被低估
REPLACE INTO 在主从架构下容易引发不一致,尤其配合 STATEMENT 格式 binlog 时。因为 DELETE + INSERT 是两条语句,从库执行顺序或并发写入可能导致中间态暴露,或被其他事务干扰。
- ROW 格式 binlog 能缓解这个问题,但日志体积变大,且仍无法规避自增 ID 跳变带来的主从差异(比如从库回放时自增值不同)
- 高并发下,REPLACE INTO 容易产生锁等待:它会对冲突行加 X 锁,然后删、再插,比单纯 UPDATE 更耗时
- 如果业务依赖“更新不改变 ID”,请立刻放弃 REPLACE INTO,改用带条件的 UPDATE 或 ON DUPLICATE KEY UPDATE










