MongoDB多请求更新文档避免数据丢失解决方案咨询
解决MongoDB并发更新数据丢失问题
问题描述
当多个并发请求更新同一文档的数值字段时,会出现数据丢失的情况:
示例:
daily_Usage初始值为0,两个请求同时读取到该值,各自加1后执行更新,最终daily_Usage结果为1,但预期应为2。
Users文档结构如下:
{ name: "abc", place: "bangalore", userLimits: { daily: 10, daily_Usage: 0 } }
原代码问题分析
原代码采用先查询后更新的逻辑,这会引入竞态条件——多个请求同时读取到相同的字段值,各自计算后覆盖更新,导致部分更新丢失。原代码:
lists.map(async (list) => { let userInfo = await Users.find({ name: "abc"}); // 假设仅一个用户,后续会改为动态获取 let query = { name: userInfo[0].name} let update = { "userLimits.daily_Usage": userInfo[0].userLimits.daily_Usage + 1 }; await Users.findOneAndUpdate(query, update) }).catch(function (error) { console.log("Error"); });
解决方案
核心是使用MongoDB的$inc原子操作符,让数据库层面完成数值递增,消除客户端的竞态条件;同时正确处理map中的异步操作。
修改后的代码
// 用Promise.all等待所有异步更新完成 await Promise.all(lists.map(async (list) => { // 直接通过$inc原子递增目标字段,无需先查询文档 await Users.findOneAndUpdate( { name: "abc" }, // 查询条件,后续可改为动态值 { $inc: { "userLimits.daily_Usage": 1 } }, // 原子性将daily_Usage加1 { new: true } // 可选配置:返回更新后的文档,不需要可省略 ); })).catch(error => { console.log("更新出错:", error); });
关键说明
$inc原子操作:MongoDB原生支持的操作,会在数据库端对指定字段执行原子递增,多个并发请求会被数据库排队处理,确保每个递增操作都生效,不会丢失更新。Promise.all处理异步队列:map返回的是多个Promise组成的数组,Promise.all会等待所有更新操作完成,并且统一捕获错误,避免单个请求失败导致流程异常。- 移除先查询步骤:不需要先获取字段当前值,直接交给数据库处理原子递增,从根源上消除竞态条件。
内容的提问来源于stack exchange,提问作者RAJENDRA H
相关产品推荐
相关产品推荐

