结论:三者均需唯一索引才生效,无索引时全无效;insert ignore静默跳过冲突但报其他错误,on duplicate key update原子更新更安全,replace into先删后插有严重副作用。

直接说结论:INSERT IGNORE、ON DUPLICATE KEY UPDATE、REPLACE INTO 都能绕过重复键错误,但语义和副作用完全不同;没建唯一索引前,三者全无效——这是 90% 的人踩坑的第一步。
必须先确认表有没有唯一约束
MySQL 只在碰到 PRIMARY KEY 或 UNIQUE KEY 冲突时才触发去重逻辑。没有对应索引,INSERT IGNORE 就是普通 INSERT,照样报 ERROR 1062。
- 查索引用:
SHOW CREATE TABLE your_table;,重点看输出里有没有PRIMARY KEY或UNIQUE KEY行,且覆盖你要判重的字段(比如想按email去重,但只有id是主键,那就不行) - 补索引要小心:
ALTER TABLE user ADD UNIQUE KEY uniq_email (email);,如果表里已有重复email,这条命令会失败,得先DELETE或GROUP BY清理 - 复合唯一索引也合法:
UNIQUE KEY uk_user_event (user_id, event_type),适合业务上“一个用户每天只记一次某类事件”这种场景
INSERT IGNORE 不是静默插入,只是静默跳过冲突行
INSERT IGNORE 只把主键/唯一键冲突降级为 warning,其他错误照常中断:字段超长、非法时间、NOT NULL 插 NULL 都会直接报错,返回值是 -1。
- 执行后立刻跑:
SHOW WARNINGS;,能看到类似Duplicate entry 'xxx' for key 'PRIMARY'的提示,这才是真正被忽略的行 -
mysql_affected_rows()返回值含义:1 = 新插入,0 = 被忽略(正常),-1 = 其他错误(真出问题了) - 别和
ON DUPLICATE KEY UPDATE混写:INSERT IGNORE ... ON DUPLICATE KEY UPDATE ...语法直接报错,二者互斥
ON DUPLICATE KEY UPDATE 更适合“存在即更新”场景
它本质是“尝试插入,冲突就改指定字段”,原子性强,不删行、不改自增 ID、不触发 DELETE 触发器,比 REPLACE INTO 安全得多。
- 更新时想用插入值,写
name = VALUES(name);想用原值加一,写count = count + 1 - 注意:它只更新冲突行本身,不会波及其他行;哪怕你
INSERT5 行,只有第 3 行冲突,也只更新第 3 行 - 如果表有多个唯一索引(比如
id主键 +email唯一键),冲突任一都触发UPDATE,但WHERE条件只按实际冲突的索引字段生效
REPLACE INTO 看似简单,实则危险
它不是“替换字段”,而是先 DELETE 再 INSERT,会改变自增 ID、重置 ON UPDATE CURRENT_TIMESTAMP 字段、触发 DELETE 和 INSERT 双重触发器——从库同步时尤其容易出问题。
- 如果只指定了部分列(如
REPLACE INTO t(id) VALUES(1)),其他列会变成NULL或默认值,不是保留原值 - 主从复制中,主库用
REPLACE,从库可能因状态不一致直接卡死在ERROR 1062,因为从库重放的是“删+插”,但删那步可能找不到行 - 高并发下,两个请求同时
REPLACE同一主键,第二个会删掉第一个刚插的行,导致数据丢失
最容易被忽略的一点:所有这些方案都依赖唯一索引的准确性。线上表结构改完索引后,务必用 SELECT 核对历史数据是否真满足唯一性——否则后续每条 INSERT IGNORE 都在 silently 掩盖脏数据。











