REPLACE INTO本质是DELETE+INSERT,非更新而是先删后插,导致自增ID变化、触发器执行两次、外键约束可能被违反,仅适用于有主键或唯一索引的表。
它不是“更新”,而是先按主键或唯一索引匹配,找到就删掉旧行,再插入新行。这意味着自增 ID 会变化、触发器会执行两次(DELETE 和 INSERT 各一次)、外键约束可能被违反(如果关联记录存在且 ON DELETE RESTRICT)。
常见错误现象:REPLACE INTO 后发现 id 字段跳变、AUTO_INCREMENT 值意外增长、触发器重复写日志。
INSERT
REPLACE INTO 语句中显式指定值,会被设为默认值(不是保留原值)REPLACE INTO 是覆盖式,而 INSERT ... ON DUPLICATE KEY UPDATE 是增量式更新。比如你只想更新 updated_at 或累加计数,用 REPLACE INTO 会导致其他字段被重置。
示例:想更新用户最后登录时间但保留昵称不变:
INSERT INTO users (id, nickname, last_login) VALUES (123, 'old_name', NOW()) ON DUPLICATE KEY UPDATE last_login = VALUES(last_login);
若用 REPLACE INTO,nickname 会变成 NULL 或空字符串(取决于定义),除非你显式带上原值。
ON DUPLICATE KEY UPDATE 不改变自增 ID,也不触发 DELETE 相关逻辑REPLACE INTO 多一次磁盘 I/O(删+插),高并发下更容易锁表REPLACE INTO 无法使用 RETURNING 子句(而 INSERT ... ON DUPLICATE KEY UPDATE 可以)真正适合 REPLACE INTO 的情况其实很窄:你明确希望“完全替换整行”,且能接受 ID 变更、默认值覆盖、触发器双触发等副作用。
常见用法:
config 表只有 key 和 value,主键为 key),每次全量覆盖注意:REPLACE INTO 不检查字段级变更——哪怕只改了一个字节,也会删旧插新。
REPLACE INTO 需要同时具备 INSERT 和 DELETE 权限。很多线上账号只开了 INSERT,执行时直接报错:ERROR 1142 (42000): REPLACE command denied to user。
在事务中使用时,如果中途失败,DELETE 已发生但 INSERT 未完成,会导致数据丢失(不像 ON DUPLICATE KEY UPDATE 那样原子性更强)。
DELETE 权限,而不是仅靠测试库权限推断REPLACE INTO 和其他 DML,尤其涉及相同主键范围时,死锁风险更高REPLACE INTO 在基于行的 binlog 中记录为 DELETE_EVENT + WRITE_ROWS_EVENT,下游解析工具需兼容真正需要“替换”语义时,先确认是否真的不能用 INSERT ... ON DUPLICATE KEY UPDATE;如果必须用 REPLACE INTO,得把自增 ID 变更、触发器行为、权限和 binlog 影响都当作设计约束来对待。