标准答案
- 唯一约束或排他约束是数据库裁决重复的事实来源;先 SELECT 判断不存在再 INSERT,在两个请求并发时仍会发生竞态。
- DO NOTHING 表示冲突时不写入,应用必须知道如何区分“已存在且等价”和“冲突但内容不应覆盖”。
- DO UPDATE 会触发更新时间、触发器、索引维护和可能的业务副作用;应使用条件更新或比较版本,避免把异常冲突静默覆盖。
- 冲突目标、RETURNING、事务隔离和错误映射要围绕业务不变量设计,尤其要明确重复请求返回的是原结果、当前结果还是冲突错误。
- 高并发下同一个唯一键会形成热点和锁等待,必要时要做请求合并、分片、退避或调整写入模型。
题目解析
upsert 的价值在于把竞争交给数据库约束,而不是在应用层做一次不可靠的存在性判断。它适合配置快照、同步游标和可以合并的状态写入。
订单、支付和账号等业务不能因为“有冲突就更新”而隐藏异常。必须区分同一幂等请求、不同内容的冲突和真正需要人工处理的业务状态。
upsert 只覆盖一张表的约束竞争;如果写入还要发消息、更新缓存或调用外部服务,仍需用幂等记录、Outbox 和可重试流程保护副作用。
常见误区
- 误区:先 SELECT 再 INSERT 就能去重。改正:使用唯一约束和 ON CONFLICT,让数据库在同一竞争点裁决。
- 误区:所有冲突都 DO UPDATE。改正:先定义冲突是否可合并,支付、账号和状态机冲突通常需要拒绝或告警。
- 误区:upsert 成功就代表外部副作用只执行一次。改正:数据库写入与消息、缓存、HTTP 调用仍需独立的幂等和补偿设计。