我有一个包含900万条记录的集合。我目前正在使用以下脚本来更新整个集合:
simple_update.js
db.mydata.find().forEach(function(data) {
db.mydata.update({_id:data._id},{$set:{pid:(2571 - data.Y + (data.X * 2572))}});
});
这是从命令行运行,如下所示:
mongo my_test simple_update.js
所以我所做的就是根据简单的计算添加一个新字段 pid 。
有更快的方法吗?这需要很长时间。
答案 0 :(得分:28)
你可以做两件事。
该链接还包含以下建议:
这是执行批量管理工作的好方法。在服务器上运行mongo,通过localhost接口连接。然后连接非常快且低延迟。这比db.eval()更友好,因为db.eval()会阻止其他操作。
这可能是你得到的最快的。您必须意识到在单个服务器上发布9M更新将是一项繁重的操作。让我们说你可以获得3k更新/秒,你还在谈论跑步近一个小时。
这不是真正的“mongo问题”,这将是硬件限制。
答案 1 :(得分:18)
我使用的是:db.collection.update method
// db.collection.update( criteria, objNew, upsert, multi ) // --> for reference
db.collection.update( { "_id" : { $exists : true } }, objNew, upsert, true);
答案 2 :(得分:3)
我不建议将{multi:true}用于更大的数据集,因为它的配置较少。
使用批量插入的更好方法。
批量操作对调度程序任务非常有用。假设您必须删除每天6个月以前的数据。使用批量操作。它速度快,不会减慢服务器速度。当您插入,删除或更新超过十亿个文档时,CPU,内存使用情况不明显。当你处理数百万个文档时,我发现{multi:true}会减慢服务器的速度(需要更多的研究。)
请参阅下面的示例。它是一个js shell脚本,也可以在服务器中作为节点程序运行。(使用npm模块shelljs或类似的实现此目的)
将mongo更新为3.2 +
更新多个唯一文档的常规方法是
let counter = 0;
db.myCol.find({}).sort({$natural:1}).limit(1000000).forEach(function(document){
counter++;
document.test_value = "just testing" + counter
db.myCol.save(document)
});
我尝试时花了310-315秒。更新一百万份文件的时间超过5分钟。
我的收藏品包含1亿多个文件,因此其他人的速度可能不同。
同样使用批量插入
let counter = 0;
// magic no.- depends on your hardware and document size. - my document size is around 1.5kb-2kb
// performance reduces when this limit is not in 1500-2500 range.
// try different range and find fastest bulk limit for your document size or take an average.
let limitNo = 2222;
let bulk = db.myCol.initializeUnorderedBulkOp();
let noOfDocsToProcess = 1000000;
db.myCol.find({}).sort({$natural:1}).limit(noOfDocsToProcess).forEach(function(document){
counter++;
noOfDocsToProcess --;
limitNo--;
bulk.find({_id:document._id}).update({$set:{test_value : "just testing .. " + counter}});
if(limitNo === 0 || noOfDocsToProcess === 0){
bulk.execute();
bulk = db.myCol.initializeUnorderedBulkOp();
limitNo = 2222;
}
});
最佳时间是8972毫安。所以平均而言,更新一百万份文件只花了10秒钟。比旧路快30倍。
将代码放在.js文件中,并以mongo shell脚本执行。
如果有人找到更好的方法,请更新。让我们以更快的方式使用mongo。
答案 3 :(得分:0)
不确定它是否会更快,但您可以进行多次更新。只需说update where _id > 0
(对于每个对象都是如此)然后将'multi'标志设置为true,它应该做同样的事情,而不必迭代整个集合。
答案 4 :(得分:0)
以Mongo 4.2
,db.collection.update()
开始可以接受聚合管道,最终允许基于另一个字段来更新/创建一个字段;从而使我们可以完全在服务器端应用这种查询:
// { Y: 456, X: 3 }
// { Y: 3452, X: 2 }
db.collection.update(
{},
[{ $set: { pid: {
$sum: [ 2571, { $multiply: [ -1, "$Y" ] }, { $multiply: [ 2572, "$X" ] } ]
}}}],
{ multi: true }
)
// { Y: 456, X: 3, pid: 9831 }
// { Y: 3452, X: 2, pid: 4263 }
第一部分{}
是匹配查询,用于过滤要更新的文档(在这种情况下为所有文档)。
第二部分[{ $set: { pid: ... } }]
是更新聚合管道(请注意方括号表示使用聚合管道)。 $set
是新的聚合运算符,别名为$addFields
。请注意如何基于同一文档中的pid
(X
)和$X
(Y
)的值直接创建$Y
。
不要忘记{ multi: true }
,否则只会更新第一个匹配的文档。