建立一个由所有文档 ID 组成的标准,然后执行更新的方法必然会导致潜在的问题。当您迭代文档列表并为每个文档发送更新操作时,在 Mongoose 中,您面临着服务器崩溃的风险,尤其是在处理大型数据集时,因为您在继续下一个数据集之前不会等待异步调用完成迭代。您实际上将构建一个未解决的操作的“堆栈”,直到这导致问题 - Stackoverflow。
举个例子,假设您有一个文档 ID 数组,您想要更新状态字段上的匹配文档:
const processedIds = [
"57a0a96bd1c6ef24376477cd",
"57a052242acf5a06d4996537",
"57a052242acf5a06d4996538"
];
您可以在哪里使用updateMany() https://mongoosejs.com/docs/api/model.html#model_Model.updateMany method
Model.updateMany(
{ _id: { $in: processedIds } },
{ $set: { status: "processed" } },
callback
);
或者对于非常小的数据集,您可以使用forEach() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach数组上的方法来迭代它并更新您的集合:
processedIds.forEach(function(id)){
Model.update({ _id: id}, { $set: { status: "processed" } }, callback);
});
以上对于小数据集来说是可以的。但是,当您面临数千或数百万个文档需要更新时,这就会成为一个问题,因为您将在循环内重复调用异步代码的服务器。
要克服这个问题,请使用异步之类的东西eachLimit https://github.com/caolan/async/blob/master/README.md#eachLimit并迭代数组,为每个项目执行 MongoDB 更新操作,同时绝不执行超过 x 个并行更新。
最好的方法是使用批量 API,这在批量处理更新方面非常有效。与对众多文档中的每一个文档调用更新操作相比,性能上的差异在于,批量 API 不是在每次迭代时向服务器发送更新请求,而是每 1000 个请求(批量)发送一次请求。
对于猫鼬版本>=4.3.0
支持 MongoDB 服务器3.2.x
, 您可以使用bulkWrite() https://docs.mongodb.org/manual/reference/method/db.collection.bulkWrite/用于更新。以下示例展示了如何执行此操作:
const bulkUpdateCallback = function(err, r){
console.log(r.matchedCount);
console.log(r.modifiedCount);
}
// Initialize the bulk operations array
const bulkUpdateOps = [], counter = 0;
processedIds.forEach(function (id) {
bulkUpdateOps.push({
updateOne: {
filter: { _id: id },
update: { $set: { status: "processed" } }
}
});
counter++;
if (counter % 500 == 0) {
// Get the underlying collection via the Node.js driver collection object
Model.collection.bulkWrite(bulkUpdateOps, { ordered: true, w: 1 }, bulkUpdateCallback);
bulkUpdateOps = []; // re-initialize
}
})
// Flush any remaining bulk ops
if (counter % 500 != 0) {
Model.collection.bulkWrite(bulkOps, { ordered: true, w: 1 }, bulkUpdateCallback);
}
对于猫鼬版本~3.8.8
, ~3.8.22
, 4.x
支持 MongoDB 服务器>=2.6.x
,您可以使用批量 API,如下所示
var bulk = Model.collection.initializeOrderedBulkOp(),
counter = 0;
processedIds.forEach(function(id) {
bulk.find({ "_id": id }).updateOne({
"$set": { "status": "processed" }
});
counter++;
if (counter % 500 == 0) {
bulk.execute(function(err, r) {
// do something with the result
bulk = Model.collection.initializeOrderedBulkOp();
counter = 0;
});
}
});
// Catch any docs in the queue under or over the 500's
if (counter > 0) {
bulk.execute(function(err,result) {
// do something with the result here
});
}