导读:本期聚焦于小伙伴创作的《PostgreSQL中ON CONFLICT DO NOTHING如何忽略重复数据插入?》,敬请观看详情。在批量写入PostgreSQL表时,唯一约束常常让重复插入直接报错中断任务。ON CONFLICT DO NOTHING作为upsert语法的分支,能在冲突发生时静默跳过该行而不抛异常。它依托唯一索引或主键判定冲突,执行计划仅做存在性校验,比先查后插减少一次往返。需要注意的是,该子句不会告知跳过了多少行,也无法区分是哪种约束命中。在日志采集、埋点落库等允许丢重的场景里,用它替代应用层去重可显著降低代码复杂度。但若业务要求冲突时更新,则应改用DO UPDATE。

在PostgreSQL里,当我们向一张带有唯一约束或主键的表插入数据时,如果待插入的行与已有行发生冲突,默认会抛出错误并终止当前语句。ON CONFLICT DO NOTHING是INSERT语句的一个可选子句,用来在检测到冲突时直接放弃该行插入,既不报错也不修改已有数据。这种机制常被称作upsert中的“仅忽略”模式,适合那些重复数据无业务危害、只求快速落地的场景。

PostgreSQL中ON CONFLICT DO NOTHING如何忽略重复数据插入?

一、基本语法与执行原理

ON CONFLICT子句必须出现在VALUES或查询部分之后,其基本写法为在INSERT末尾追加ON CONFLICT (冲突列) DO NOTHING,或者省略列名写成ON CONFLICT DO NOTHING由数据库自动匹配所有唯一约束。PostgreSQL在执行时会先按照指定的唯一索引进行冲突检测,如果发现重复键,就跳过该元组,继续处理后续记录。由于不需要回表更新,其开销通常小于DO UPDATE。

下面的例子创建一张用户标签表,并对user_id和tag做联合唯一约束,然后演示如何安全插入:

CREATE TABLE user_tag (
    id serial PRIMARY KEY,
    user_id int NOT NULL,
    tag text NOT NULL,
    UNIQUE (user_id, tag)
);

-- 重复插入相同(user_id, tag)时不会报错
INSERT INTO user_tag (user_id, tag)
VALUES (1001, 'vip')
ON CONFLICT (user_id, tag) DO NOTHING;

-- 也可省略冲突列,依赖表上已定义的唯一约束
INSERT INTO user_tag (user_id, tag)
VALUES (1001, 'vip')
ON CONFLICT DO NOTHING;

从执行计划看,DO NOTHING在冲突时仅做索引探测,不会获取行锁去修改堆数据,因此在高并发写入时锁竞争更小。但若表中存在多个唯一约束,省略列名可能导致意料之外的忽略,建议明确写出冲突目标。

二、与应用层去重对比

很多团队会在代码里先执行SELECT判断是否存在,再决定INSERT,这种做法在并发下容易因竞态产生重复或报错,且每次写入多出一次网络往返。使用ON CONFLICT DO NOTHING把判断下推到数据库内核,既能利用唯一索引的高效查找,也天然具备原子性。

以下Node.js伪代码展示了两种写法差异:

// 应用层先查后插(有并发隐患)
const exists = await db.query('SELECT 1 FROM user_tag WHERE user_id=$1 AND tag=$2', [uid, tag]);
if (!exists.rows.length) {
  await db.query('INSERT INTO user_tag (user_id, tag) VALUES ($1,$2)', [uid, tag]);
}

// 直接使用ON CONFLICT DO NOTHING
await db.query(
  'INSERT INTO user_tag (user_id, tag) VALUES ($1,$2) ON CONFLICT (user_id, tag) DO NOTHING',
  [uid, tag]
);

后者代码更简洁,也避免了两次请求。在压测中,单条插入的QPS通常能提升两到三成。不过DO NOTHING不会返回被忽略的行数明细,如果业务需要统计跳过量,可以通过INSERT ... RETURNING结合CTE先试插再统计,但那就偏离了最简用法。

三、适用场景与注意事项

典型适用场景包括:日志型数据补录、消息幂等消费中的落库、离线批量导入等。这些场景的特点是允许重复丢弃,且希望写入不被异常打断。相反,若冲突时需要把旧记录的时间字段刷新,或累加计数,就必须改用ON CONFLICT DO UPDATE。

还需注意,DO NOTHING只忽略唯一约束和排除约束冲突,不满足CHECK约束仍会报错;另外在可延迟约束设置为DEFERRABLE时,冲突检测会推迟到事务提交,这期间看似插入成功,提交时仍可能失败。因此设计表结构时要明确约束的即时性。

-- 错误示例:列类型不符或违反非空约束仍会报错
INSERT INTO user_tag (user_id, tag)
VALUES (null, 'vip')
ON CONFLICT DO NOTHING;  -- 违反user_id NOT NULL,不是唯一冲突,会直接异常

综合来看,ON CONFLICT DO NOTHING是PostgreSQL提供的轻量级冲突处理手段。理解其基于唯一索引的判定逻辑,并在合适的幂等写入场景中使用,可以省掉大量应用层容错代码,同时维持较好的写入性能。

PostgreSQLON_CONFLICT_DO_NOTHINGupsert修改时间:2026-08-11 00:06:24

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。