PostgreSQL upsert 返回已存在记录这个功能有什么用?
本期播客
PostgreSQL upsert 返回已存在记录这个功能有什么用?
在数据库开发的漫长岁月中,有一个经典逻辑像幽灵一样困扰着每一位程序员:“如果这条记录不存在就插入,如果存在就把它查出来给我。”
这个看似简单的需求,在过去几十年的 SQL 标准里,竟然是一个极其昂贵且充满陷阱的操作。然而,2026 年 2 月,PostgreSQL 再次祭出神来之笔:正式支持 INSERT ... ON CONFLICT DO SELECT。
别被这短短的一行 SQL 骗了,这绝对不是什么“语法糖”,这是一场关于原子性与 网络往返(RTT) 的效率革命。
https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=88327092ff06c48676d2a603420089bf493770f3
一、 痛点:被逼出来的“多表单病”与 Race Condition
在没有 DO SELECT 之前,我们要实现“不存在则插入,存在则返回”通常有两种选择,但它们都是“坑”:
先 SELECT,再 INSERT:
风险: 经典的竞态条件(Race Condition)。在并发环境下,两个进程同时发现记录不存在,同时尝试插入,必然导致一个成功,另一个报唯一键冲突。
INSERT ... ON CONFLICT DO UPDATE:
代价: 为了能拿到返回值,即便数据没变,你也不得不触发一次 UPDATE。后果: 产生大量的 Dead Tuples(死亡元组) ,无端引发 VACUUM 压力,甚至导致索引膨胀。为了读一个值而去写一次盘,这在架构设计上简直是“杀鸡用牛刀”。
二、 技术剖析:原子性的“降维打击”
此次更新支持 ON CONFLICT DO SELECT [FOR UPDATE/SHARE],配合 RETURNING 子句,完美解决了上述矛盾。
1. 彻底消灭网络往返(1 vs N)
从第一性原理来看,数据库性能的瓶颈往往不在 CPU,而在 IO 和网络。
旧逻辑: 1. 尝试插入 -> 2. 捕获异常 -> 3. 重新查询。这至少涉及 2 到 3 次 数据库交互。 新逻辑: 一条 SQL 发过去,数据库内核在检测到冲突的瞬间,直接在内存中转向读取逻辑并返回。性能提升不是百分比级别的,是倍数级别的。
2. 带有“防御性锁定”的读取
这次更新最硬核的是支持 FOR UPDATE。这意味着在冲突发生的瞬间,你不仅拿到了旧数据,还顺手给它加了锁。这对于分布式锁、库存预扣、唯一序列生成等场景,简直是教科书级的原子操作。
三、 权威支撑:为什么它是高并发系统的救星?
1. 权威案例:社交平台的“关注”逻辑
想象一下某社交平台(如微博、X)的“关注”功能:用户连续点击关注,或者多个客户端同时操作。
场景: 插入一条“关注关系”。如果已关注,则返回关注时间。
数据证明: 在超高并发下,由于ON CONFLICT DO UPDATE导致的行锁竞争和页面清理(Vacuum),吞吐量往往会遇到瓶颈。改为DO SELECT后,写操作变成了纯读操作,行锁持有时间缩短了 60% 以上。
2. ID 生成器与映射表
在处理海量短链接或标签映射(Tagging System)时,通常需要:输入一个字符串,返回其对应的唯一自增 ID。
旧痛: 每天处理 10 亿次映射,如果 90% 是重复请求,那么旧版的 DO UPDATE会产生 9 亿次无效写入。新利器: DO SELECT让这 9 亿次操作变成了纯内存扫描,存储成本和 IOPS 直接下降了一个数量级。
四、 逻辑的崩塌:如果这还不够快?
前提假设: 我们假设“原子性”是所有业务的底线。
条件崩塌: 如果你的业务对数据一致性要求不高,但对响应延迟有极致追求(例如日志打点、不计后果的计数器),那么即便是 DO SELECT 也会因为主键冲突检查而产生开销。
引申观点: 在这种情况下,你需要的可能不是更强的 UPSERT,而是 布隆过滤器(Bloom Filter) 或 缓存预检。但在绝大多数金融、电商、CRM等对数据准确性有硬性要求的领域,ON CONFLICT DO SELECT 就是目前的“最优解”。
结语
PostgreSQL 再次证明了为什么它是“开发者的宠儿”。它敏锐地察觉到了程序员在编写 if-else 逻辑时的痛苦,并将这种痛苦在内核层面予以消解。
INSERT ... ON CONFLICT DO SELECT 的出现,标志着 PG 在处理 幂等性接口(Idempotent API) 时,已经达到了工业界的顶峰。它不仅省下了代码量,更省下了昂贵的服务器 IOPS 和 DBA 的头发。
那么问题来了:既然一条 SQL 就能优雅解决“插不进去就查出来”的问题,你代码里那些臃肿的 try-catch 重试逻辑,是不是该删一删了?
最后附一则济南的线下峰会消息:
PostgreSQL & IvorySQL 2026 年度峰会将于4月份在济南召开,这是目前国内规模最大的PG峰会.
我是新特性分论坛出品人,欢迎报名参与分享,主委会可解决分享嘉宾住宿和路费。
报名地址: https://jsj.top/f/uebqBc
议题方向:
PostgreSQL 新功能 PostgreSQL 内核机制与性能优化 PostgreSQL 扩展程序 AI + PostgreSQL 技术实践 云原生PostgreSQL或IvorySQL PostgreSQL 用户实践 IvorySQL 兼容性与生态实践 基准测试与性能调优 高可用性技术 …… 任何与PostgreSQL或IvorySQL相关的内容