【发布时间】:2017-10-30 02:15:59
【问题描述】:
我需要在大约 500K 个文档的集合中的每个文档上创建一个新字段 sid。每个 sid 都是唯一的,并且基于该记录的现有 roundedDate 和 stream 字段。
我正在使用以下代码:
var cursor = db.getCollection('snapshots').find();
var iterated = 0;
var updated = 0;
while (cursor.hasNext()) {
var doc = cursor.next();
if (doc.stream && doc.roundedDate && !doc.sid) {
db.getCollection('snapshots').update({ "_id": doc['_id'] }, {
$set: {
sid: doc.stream.valueOf() + '-' + doc.roundedDate,
}
});
updated++;
}
iterated++;
};
print('total ' + cursor.count() + ' iterated through ' + iterated + ' updated ' + updated);
起初它运行良好,但在几个小时后,大约 100K 记录它出错:
Error: getMore command failed: {
"ok" : 0,
"errmsg": "Cursor not found, cursor id: ###",
"code": 43,
}: ...
【问题讨论】:
-
我原以为你最大的问题是
doc.stream && doc.roundedDate && !doc.sid这一行。您只是传递.find()而根本没有查询表达式,而是在代码中过滤 all 文档。通过移动到查询.find({ "roundedDate": { "$exists": true }, "stream": { "$exists": true }, "sid": { "$exists": false } })让数据库 完成工作。您还应该每 500 个左右的项目使用.bulkWrite(),而不是实际执行服务器上的每个语句。 -
这两件简单的事情 1. 不要迭代不必要的项目,2. 使用批量提交来避免服务器确认开销。应该从您当前的流程中节省“几个小时”。您可以进行其他更改以使光标保持活动状态,但最好的办法基本上是减少处理时间。甚至可以通过
_id上的“范围”来分解文档的选择,以进一步减少可能的选择。 -
很棒的信息@NeilLunn。我正在查看 bulkWrite() - 在我使用 bulkWrite() 和 updateMany() 的特殊情况下的问题是我无法访问每条记录,并且无法创建 sid 字段。不过,我一直在寻找这样的方法。我可以使用 mongoose 在数组中本地组装/更新所有记录,但我找不到在数据库中对它们进行 batcg 插入/更新/覆盖的方法。
-
我想你误解了
.bulkWrite()的用法。以this answer 为例。弄乱光标超时只会带你到目前为止。您真正需要做的是减少开销。 -
@NeilLunn 感谢您指出这一点,我完全错过了这一点,实际上查询可以在游标过期之前执行,特别是如果有可用于匹配
.find(...)查询的索引。
标签: node.js mongodb mongodb-query database-cursor