INSERT IGNORE仅对PRIMARY KEY和UNIQUE约束冲突生效,外键、NULL或CHECK失败时直接报错;ON DUPLICATE KEY UPDATE应只更新必要字段,避免无差别赋值;唯一索引需明确定义且注意NULL可重复特性;REPLACE INTO代价高,慎用。

INSERT IGNORE 为什么有时不生效
它只对 PRIMARY KEY 和 UNIQUE 约束冲突起作用,遇到外键错误、NULL 冲突或 CHECK 约束失败时直接报错,不是“静默跳过”。MySQL 8.0.19+ 还会忽略 INSERT ... ON DUPLICATE KEY UPDATE 中的非唯一索引冲突(除非显式定义为 UNIQUE)。
实操建议:
- 确保目标字段上有明确的
UNIQUE索引,而不是仅靠业务逻辑“认为它唯一” - 避免在
INSERT IGNORE后依赖ROW_COUNT()判断是否插入成功——它返回 0 既可能是重复,也可能是被忽略的其他错误 - 如果表有多个唯一约束(比如
email和phone都唯一),INSERT IGNORE会因任一冲突而跳过整行,无法区分是哪个字段导致的
ON DUPLICATE KEY UPDATE 怎么写才安全
它只响应由 PRIMARY KEY 或 UNIQUE 索引触发的重复键错误,且更新动作必须限定在冲突行上。常见误用是把 UPDATE 子句写成无条件覆盖,结果意外改掉本不该动的字段。
实操建议:
- 只更新真正需要“去重时同步”的字段,比如
updated_at = NOW(),不要写name = VALUES(name)这类无差别赋值 - 慎用
VALUES(col)——它取的是本次INSERT语句中该列的原始值,不是“新值优先”,也不是“旧值保留” - 如果要实现“存在则不变,不存在则插入”,就别用这个语法;它本质是“存在则更新”,不是条件插入
唯一索引建在哪几个字段上才算真正防重
单字段唯一索引最简单,但业务中常需多字段组合唯一(如 (user_id, order_type))。关键点在于:索引列顺序影响查询效率,但不影响唯一性判定;NULL 值在唯一索引中可重复(MySQL 允许多个 NULL),这点极易被忽略。
实操建议:
- 把高频查询条件放在索引左侧,比如经常按
tenant_id+code查,就建UNIQUE(tenant_id, code),别反过来 - 如果业务要求“某个字段为空时也需唯一校验”,得提前用
COALESCE(col, 'NULL_PLACEHOLDER')计算列或加检查约束,不能依赖默认唯一索引行为 - 删除旧唯一索引再建新索引时,注意锁表时间——大表上操作可能阻塞写入,建议在低峰期执行
ALTER TABLE ... ADD UNIQUE
REPLACE INTO 的代价比你想的高
它不是“更新”,而是先 DELETE 再 INSERT,会触发两次自增 ID 分配、外键级联操作、触发器执行,并可能导致主从延迟加剧。在有外键引用或审计日志依赖主键连续性的场景下,风险尤其明显。
实操建议:
- 除非明确需要“强制替换整行并重置自增ID”,否则一律用
INSERT ... ON DUPLICATE KEY UPDATE替代 - 检查表是否有
ON DELETE CASCADE外键——REPLACE INTO会真实触发一次删除,连带删掉子表数据 - 监控
Handler_delete和Handler_write状态变量,若发现比例异常高,很可能误用了REPLACE INTO
真正难的不是选哪个语法,而是想清楚“重复”的定义到底由哪些字段决定、NULL 算不算参与比较、以及冲突发生时系统该做什么——这些必须在建索引前就定死,而不是靠 SQL 临时补救。

















