MongoDB如何给集合所有文档更新随机值并统计增量总和
你原有的遍历逐条调用save的实现,每次更新都会发起一次独立的数据库请求,大量IO开销导致运行速度慢;而直接用updateMany时随机值是在Node.js层提前计算的,只会生成一次,所以所有文档都会加同一个值。以下是两种可行的高性能解决方案:
方案一:批量写入(BulkWrite),全版本兼容,精度最高
该方案只需查询所有用户ID,在应用层生成每个用户的随机增量,一次性提交所有更新操作,相比逐条save性能提升数十倍,且总和计算完全精确。
const earnMoneyForUsers = async (batchSize = 5000) => { // 仅查询用户ID,减少数据传输开销 const userIds = await User.find().distinct('_id'); let totalEarnedMoney = 0; // 分批提交避免单次请求过大,适合百万级以上的集合 for (let i = 0; i < userIds.length; i += batchSize) { const currentBatch = userIds.slice(i, i + batchSize); const bulkOps = currentBatch.map(userId => { const earnedMoneyByUser = Math.floor(Math.random() * 100); const increment = earnedMoneyByUser * 0.98; // 等价于扣除2%手续费后的增量 totalEarnedMoney += increment; return { updateOne: { filter: { _id: userId }, update: { $inc: { weeklyEarnedMoney: increment, totalEarnedMoney: increment } } } }; }); await User.bulkWrite(bulkOps); } return totalEarnedMoney; };
方案二:MongoDB 4.4+ 数据库端聚合更新,性能最优
如果你的MongoDB版本 >= 4.4,可以直接在数据库层使用内置的$rand函数生成每个文档的独立随机值,无需将用户ID拉取到应用层,性能更高。需要注意如果需要总和和更新值严格一致,需要搭配事务使用,普通场景下误差可以忽略。
const earnMoneyForUsers = async () => { // 执行聚合更新,每个文档生成独立随机增量 await User.updateMany({}, [ { $addFields: { earnedMoneyByUser: { $floor: { $multiply: [{ $rand: {} }, 100] } } } }, { $set: { increment: { $multiply: ["$earnedMoneyByUser", 0.98] } } }, { $set: { weeklyEarnedMoney: { $add: ["$weeklyEarnedMoney", "$increment"] }, totalEarnedMoney: { $add: ["$totalEarnedMoney", "$increment"] } } }, { $unset: ["earnedMoneyByUser", "increment"] } ]); // 聚合统计所有增量的总和 const sumRes = await User.aggregate([ { $group: { _id: null, total: { $sum: { $multiply: [{ $floor: { $multiply: [{ $rand: {} }, 100] } }, 0.98] } } } } ]); return sumRes[0].total; };
内容的提问来源于stack exchange,提问作者basedman
相关产品推荐
相关产品推荐

