MongoDB原子操作:无锁解决并发卡片状态更新竞态问题
MongoDB单操作解决并发下的卡片ACTIVE状态设置竞态问题
需求说明
- 并发场景中,针对指定用户的Card集合:
- 检查是否存在
status为ACTIVE的文档; - 若不存在,将预先选定的现有文档的
status设为ACTIVE; - 若已存在
ACTIVE文档,则不做任何修改;
- 检查是否存在
- 必须通过单个MongoDB操作完成,避免实现额外锁机制,且无法修改触发该操作的前置流程;
- 此前的实现因竞态条件,导致并发调用时多个文档被误设为
ACTIVE。
尝试过的方案
使用findOneAndUpdate操作(也试过findOneAndReplace),代码如下:
const query = { userId: 'user-id-1', status: 'ACTIVE', }; Card.findOneAndUpdate( query, { $set: { _id: 'chosen-id', // 预先选定的要更新的卡片ID userId: 'user-id-1', status: 'ACTIVE', }, }, { upsert: true, returnNewDocument: true, } );
存在的问题
- 当未找到
ACTIVE状态的卡片时,无法更新指定的现有文档; - 开启
upsert: true会自动创建新文档,不符合“仅更新现有文档”的要求。
示例数据与预期结果
初始集合数据
{ "_id": "card-id-1", "userId": "user-id-1", "status": "READY" } { "_id": "card-id-2", "userId": "user-id-1", "status": "CANCELLED" } { "_id": "card-id-3", "userId": "user-id-1", "status": "READY" } { "_id": "card-id-4", "userId": "user-id-1", "status": "READY" }
首次执行预期结果(选定card-id-4为目标)
{ "_id": "card-id-1", "userId": "user-id-1", "status": "READY" } { "_id": "card-id-2", "userId": "user-id-1", "status": "CANCELLED" } { "_id": "card-id-3", "userId": "user-id-1", "status": "READY" } { "_id": "card-id-4", "userId": "user-id-1", "status": "ACTIVE" }
再次执行预期结果
集合保持上述状态不变(因已有ACTIVE卡片)。
核心问题
MongoDB是否支持通过单个操作实现上述行为?可升级至任意版本的MongoDB。
内容的提问来源于stack exchange,提问作者Dez
相关产品推荐
相关产品推荐

