Oracle如何原子实现select for update不存在则插入默认值的并发需求
实现方案说明
该需求完全可以通过单条(或低版本数据库配合1次额外查询)SQL实现,可解决并发冲突、无效插入过多的问题,具体实现如下:
核心逻辑
利用主键的唯一性约束,结合数据库原生的原子UPSERT(插入或更新)语义,在同一条SQL中完成「不存在则插入默认值、存在则加行锁」的操作,全程由数据库原子执行,不会出现并发插入冲突,锁粒度与SELECT FOR UPDATE一致。
不同数据库的实现示例
1. MySQL 8.0.20及以上版本(支持RETURNING子句)
单条SQL即可完成加锁+返回value的全流程:
INSERT INTO 表名 (k, value) VALUES ('目标k值', '默认value值') ON DUPLICATE KEY UPDATE k = k -- 无实际数据变更,仅触发行锁,效果与SELECT FOR UPDATE一致 RETURNING value;
- 若k不存在:自动插入带默认值的新行,加排他锁,返回插入的默认value
- 若k存在:触发行锁,返回现有行的value
- 并发请求会阻塞到第一个事务提交,后续请求直接获取已插入的行数据,完全符合需求。
2. 低版本MySQL(不支持RETURNING子句)
需在同一事务内执行两条SQL,因第一条UPSERT已加行锁,不会产生并发问题:
第一步执行原子UPSERT加锁:
INSERT INTO 表名 (k, value) VALUES ('目标k值', '默认value值') ON DUPLICATE KEY UPDATE k = k;
第二步查询获取value:
SELECT value FROM 表名 WHERE k = '目标k值';
3. PostgreSQL版本
语法稍有区别,同样支持单条SQL完成操作:
INSERT INTO 表名 (k, value) VALUES ('目标k值', '默认value值') ON CONFLICT (k) DO UPDATE SET k = excluded.k RETURNING value;
方案优势
- 彻底避免拆分SELECT+INSERT时出现的唯一键冲突报错问题
- 仅在k确实不存在时执行插入,存在时仅触发轻量无变更更新,性能开销远低于提前执行
INSERT IF NOT EXISTS的方案,非常适合插入概率极低的场景 - 仅锁目标k对应的行,不影响其他行操作,并发性能好
注意事项
- 需确保k字段是主键或有唯一索引,否则
ON DUPLICATE KEY UPDATE/ON CONFLICT无法正确触发 - 所有对该表目标k的读写操作需遵循这套加锁逻辑,避免无锁插入导致的冲突
内容的提问来源于stack exchange,提问作者SNJ
相关产品推荐
相关产品推荐

