INSERT IGNORE仅在表存在PRIMARY KEY或UNIQUE约束时生效,遇主键/唯一索引冲突静默跳过、不报错不更新,其他错误(如字段超长、类型不匹配)仍报错;批量插入逐行判断,返回实际新增行数,不保证事务原子性。

INSERT IGNORE 不是万能去重开关,它只对主键或唯一索引冲突静默跳过,其他错误照常报错,且不更新已有数据。
INSERT IGNORE 什么情况下真正生效
它只在表已定义 PRIMARY KEY 或 UNIQUE 约束的前提下起作用。没有对应索引,INSERT IGNORE 和普通 INSERT 行为完全一致——该报错还报错。
- 常见错误现象:执行后
ROW_COUNT()返回 0,但查不到新数据,日志也没报错,其实是被忽略了 - 必须确保冲突字段(如
msg_id、username)已建UNIQUE KEY,否则INSERT IGNORE不会拦截任何重复 - 它对所有唯一索引生效,不只是主键;比如
UNIQUE KEY uk_email(email)冲突也会被忽略 - 字段超长、类型不匹配、NOT NULL 插入 NULL 等非唯一性错误,仍会触发报错,
IGNORE不管这些
批量插入时的行级行为容易误判
INSERT IGNORE INTO t(a,b) VALUES (1,2),(2,3),(1,4); 中,第一行和第三行都撞了 a=1 的主键,但 MySQL 是逐行判断:第一行被跳过,第二行正常插入,第三行再被跳过。最终 ROW_COUNT() 返回 1,不是 0 也不是 2。
- 不会“整批失败”,也不会“全部跳过”——这是最常被误解的一点
- 返回的受影响行数 = 实际新增行数,不是“总行数减去冲突数”
- 在事务中混用时,前面成功的行不会因后面某行冲突而回滚,
INSERT IGNORE本身不提供原子性保障 - 若需严格原子性(要么全插成功,要么全不插),得配合
SELECT ... FOR UPDATE或应用层加锁
别把它当 UPDATE 用
INSERT IGNORE 后面不能跟 UPDATE 子句,语法直接报错。它只做一件事:有冲突就跳过,没冲突就插入。想更新已有记录的字段,必须换方案。
- 要“存在则更新指定字段”,用
INSERT ... ON DUPLICATE KEY UPDATE,例如:INSERT INTO users(id,name) VALUES(1,'Alice') ON DUPLICATE KEY UPDATE name=VALUES(name), updated_at=NOW() - 要“存在则删旧插新”,才考虑
REPLACE INTO,但注意它会重置自增 ID、触发两次触发器、破坏外键关联 -
INSERT IGNORE不触发任何触发器,也不改变原记录任何字段值,安全但功能受限 - 如果业务需要幂等写入(如消费记录去重),它很合适;如果需要同步刷新状态字段,它就不够用了
监控和调试时容易漏掉的关键点
MySQL 默认开启 STRICT_TRANS_TABLES 时,某些隐式转换(比如字符串转数字失败)仍可能让 INSERT IGNORE 报错,而不是静默跳过。更隐蔽的是:警告(Warning)默认不输出,SHOW WARNINGS 才能看到 Duplicate entry 提示,而很多 ORM 或连接池会丢弃警告信息。
- 务必检查
ROW_COUNT()返回值,不能只看是否报错 - 在开发环境开启
sql_mode中的STRICT_ALL_TABLES并测试边界 case - 线上建议把
log_warnings=2加入 MySQL 配置,让重复键警告写入 error log - 用
LOAD DATA INFILE IGNORE做批量导入时,行为一致,但同样依赖底层索引定义


















