INSERT IGNORE不能解决分布式幂等写入,因其仅静默跳过唯一冲突且返回0行影响,无法区分“首次插入”与“重复忽略”,导致业务逻辑误判;必须配合唯一索引与应用层幂等Key(如Redis预校验)才能保障语义正确。

MySQL INSERT IGNORE 为什么不能解决分布式幂等写入
因为 INSERT IGNORE 只在违反 UNIQUE KEY 时静默跳过,但不返回“是否真的插入了”,更无法区分“本次是首次写入”还是“重复请求被忽略”。在分布式场景下,上游常需根据写入结果做后续动作(比如发消息、更新状态),仅靠忽略会丢失语义。
常见错误现象:服务收到重复请求,INSERT IGNORE 成功返回,但业务逻辑误判为“新数据已落库”,导致下游重复消费或状态错乱。
- 唯一键必须覆盖所有幂等维度(如
order_id+source_system,不能只建在order_id上) -
INSERT IGNORE不触发AUTO_INCREMENT增长,但会消耗自增 ID(尤其用innodb_autoinc_lock_mode=1时) - 它对
PRIMARY KEY和UNIQUE约束都生效,但对NOT NULL或检查约束无效
PostgreSQL 的 INSERT ... ON CONFLICT DO NOTHING 真正可用吗
比 MySQL 的 INSERT IGNORE 更可控:它能明确表达“冲突字段 + 动作”,且支持返回行(配合 RETURNING),可判断是否真正插入成功。
但要注意:默认不锁住冲突范围外的间隙(gap lock),高并发下可能因幻读导致多次“以为没冲突”而实际插入失败;若用 ON CONFLICT ON CONSTRAINT 指定具体约束名,性能更好、语义更稳。
立即学习“go语言免费学习笔记(深入)”;
- 必须确保冲突字段上有
UNIQUE INDEX(不能只靠UNIQUE约束,否则索引不可用) -
RETURNING id, created_at可直接拿到插入行的值,避免再查一次数据库 - 不要写
ON CONFLICT (col) DO NOTHING而不加约束名——PostgreSQL 8.4+ 支持,但某些旧驱动(如lib/pqv1.10 以下)解析异常
Go 里用 sqlx 或 pgx 执行条件插入的典型写法
核心是让 SQL 层承担幂等判断,Go 层只处理结果。不要在 Go 里先 SELECT 再 INSERT(竞态不可避免),也不要依赖事务重试掩盖问题。
以 pgx 为例,正确姿势是绑定冲突字段、用 RETURNING 拿结果:
sql := `INSERT INTO orders (id, user_id, amount, status)
VALUES ($1, $2, $3, 'pending')
ON CONFLICT (id) DO UPDATE SET status = EXCLUDED.status
RETURNING id, created_at, status`
var order Order
err := pool.QueryRow(ctx, sql, id, uid, amt).Scan(&order.ID, &order.CreatedAt, &order.Status)
关键点:
- 用
DO UPDATE SET ... = EXCLUDED.*而非DO NOTHING,可统一处理“首次插入”和“重复更新”两种语义 -
EXCLUDED是 PostgreSQL 关键字,代表本次想插入但被拦截的那行数据 - 若只需判断是否新增,
Scan成功即表示“有行返回”,失败则可能是冲突后无返回(DO NOTHING)或真错误
Redis + DB 双写方案中,原子性怎么保
纯 Redis setnx 不能替代 DB 唯一键,它只提供“请求入口层”的快速拦截,无法防止 DB 层因网络超时、重试等导致的重复落库。真正的原子性必须由 DB 主键/唯一索引兜底。
典型组合是:Redis 记录“已接收请求 ID”(带 TTL),DB 写入仍走唯一键约束。两者不是替代关系,而是分层防御。
- Redis key 生效时间建议 ≥ DB 写入最大耗时 × 2(例如 DB P99 是 200ms,TTL 设 500ms)
- 不要用
SET key val NX EX ttl后再查 DB——中间存在窗口期;应始终以 DB 唯一键为准,Redis 只作优化 - 如果 DB 报
unique_violation错误,说明 Redis 漏了或过期了,此时必须接受该错误并走幂等逻辑(如查状态、返回已有结果)
复杂点在于冲突维度多的时候(比如“用户当天第 N 笔订单”),DB 唯一键难设计,这时得靠应用层加分布式锁 + 乐观锁组合,但锁粒度和释放时机极易出错。


















