在 PostgreSQL 等支持 upsert 的数据库中,ON CONFLICT DO NOTHING 是实现写入幂等性的轻量方案。它依赖于表中已存在的唯一约束或唯一索引,当插入数据违反约束时直接忽略该行,而不是报错或更新。

为什么需要幂等写入
在消息队列消费、定时任务重跑、接口重试等场景中,同一批数据可能被反复提交。如果直接执行 INSERT,第二次就会因为主键或唯一键冲突而失败;如果先查再插,又会存在并发下的竞态问题。使用 upsert 配合 DO NOTHING,可以让重复请求安全地变成无操作。
基础表结构与约束
要实现冲突跳过,必须先有唯一约束。下面创建一个用户积分表,以 user_id 作为唯一键:
CREATE TABLE user_score (
user_id BIGINT PRIMARY KEY,
score INT NOT NULL DEFAULT 0,
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
这里的 user_id 是主键,天然具备唯一约束,可以作为 ON CONFLICT 的冲突目标。
标准幂等插入写法
使用 ON CONFLICT (冲突列) DO NOTHING 即可实现幂等:
INSERT INTO user_score (user_id, score, updated_at) VALUES (1001, 50, NOW()) ON CONFLICT (user_id) DO NOTHING;
当 user_id 为 1001 的记录已存在时,该语句不会插入新行,也不会报错,事务正常提交。
批量写入的幂等保障
批量场景同样适用,一次插入多行,冲突的行自动跳过:
INSERT INTO user_score (user_id, score, updated_at)
VALUES
(1001, 50, NOW()),
(1002, 30, NOW()),
(1003, 80, NOW())
ON CONFLICT (user_id) DO NOTHING;
复合唯一约束下的写法
如果幂等依据是多列组合,需要先建复合唯一索引:
CREATE TABLE order_log (
order_id BIGINT,
item_id BIGINT,
qty INT,
CONSTRAINT uk_order_item UNIQUE (order_id, item_id)
);
插入时指定约束名或列组合:
INSERT INTO order_log (order_id, item_id, qty) VALUES (9001, 2001, 2) ON CONFLICT ON CONSTRAINT uk_order_item DO NOTHING;
注意事项
- DO NOTHING 不会更新已有记录,若需同步最新值应使用 DO UPDATE。
- 冲突目标必须对应已存在的唯一索引或主键,否则语法报错。
- 在 RR 及以上隔离级别下仍安全,不会因并发插入导致异常。
合理运用 ON CONFLICT DO NOTHING,可以用最简 SQL 成本换来实现可靠的幂等写入,特别适合日志、计数、配置类数据的去重落库。
SQLupsertON_CONFLICT_DO_NOTHING修改时间:2026-07-26 01:09:20