insert ignore跳过重复键冲突并作为警告处理,replace into执行删+插导致id变更,on duplicate key update精准更新指定字段且不改变id。

MySQL中INSERT IGNORE遇到重复主键或唯一索引时的行为
INSERT IGNORE 本质是把重复键冲突当作警告(warning)而非错误(error),语句继续执行,跳过冲突行。它适合“只插入新数据、不覆盖旧值”的场景,比如日志去重、缓存预热。
常见错误现象:Duplicate entry 'xxx' for key 'PRIMARY' 不报错,但mysql_affected_rows()返回 0(该行未插入),容易误判为全部成功;用SHOW WARNINGS才能看到被忽略的记录。
实操建议:
- 必须确保表有主键或唯一索引,否则
IGNORE无意义 - 不要依赖
affected_rows == 行数判断是否全量插入成功 - 批量插入时,单条语句最多插入 1000 行左右(受
max_allowed_packet限制),超长需分批 - 示例:
INSERT IGNORE INTO users (id, name, email) VALUES (1,'a','a@x'),(2,'b','b@x');
REPLACE INTO在重复键存在时的实际执行逻辑
REPLACE INTO 并不是“更新”,而是“删+插”:先尝试插入,若发现主键/唯一索引冲突,则先DELETE旧行,再INSERT新行。这意味着自增 ID 会变化、外键级联删除可能触发、触发器会执行两次(DELETE + INSERT 各一次)。
使用场景:需要强制刷新整行内容,且能接受 ID 变更与额外开销。不适合有强外键约束或审计要求保留原始创建时间的表。
实操建议:
- 确认表引擎支持(MyISAM 和 InnoDB 都支持,但 MEMORY 不推荐)
- 检查是否有
ON DELETE CASCADE外键,避免误删关联数据 - 如果字段含
created_at DEFAULT CURRENT_TIMESTAMP,REPLACE会导致该字段重置,而INSERT IGNORE不会动已有行 - 示例:
REPLACE INTO users (id, name, email) VALUES (1,'new_a','a@x');
—— 若 id=1 已存在,原记录被删,新记录插入,id 可能变(InnoDB 下自增 ID 通常递增不复用,但逻辑上已不是原行)
ON DUPLICATE KEY UPDATE才是真正的“存在则更新”
相比前两者,INSERT ... ON DUPLICATE KEY UPDATE 是精准控制更新字段的方案:冲突时只更新指定列,不删行、不改自增 ID、不触发 DELETE 相关逻辑。性能更好,语义更清晰。
容易踩的坑:
- UPDATE 子句里不能直接引用 VALUES() 外的变量,必须显式写
VALUES(col_name)来获取本次插入值 - 若多个唯一索引同时冲突(如主键 + 唯一邮箱),仍只触发一次 UPDATE,不是每个索引各更新一遍
-
affected_rows返回 1(新插入)或 2(更新了已有行),可据此区分操作类型 - 示例:
INSERT INTO users (id, name, email, updated_at) VALUES (1,'a','a@x',NOW()) ON DUPLICATE KEY UPDATE name=VALUES(name), updated_at=NOW();
批量插入时三者性能与事务安全差异
三者都支持多值INSERT语法,但底层处理不同:IGNORE和REPLACE在遇到第一个冲突时就跳过或删插整行,而ON DUPLICATE KEY UPDATE对每行独立判断冲突并决定插入或更新。
关键影响点:
- 事务中使用
REPLACE可能造成间隙锁范围扩大(因先 DELETE 再 INSERT),并发插入易阻塞;IGNORE和ON DUPLICATE KEY UPDATE锁行为更轻量 -
REPLACE在大表上批量执行可能引发大量 binlog 日志(删+插双写),复制延迟风险更高 - 如果业务要求“部分失败不中断”,
IGNORE最简单;若要“冲突时合并字段”,必须用ON DUPLICATE KEY UPDATE;REPLACE仅在明确需要清除旧状态时才合理
真正容易被忽略的是:没有唯一约束的字段上做ON DUPLICATE KEY UPDATE完全无效——它只响应主键或UNIQUE索引冲突,不是按值查重。别指望它替代MERGE或应用层 upsert 逻辑。










