Node.js MongoDB-错误:getMore命令失败:找不到光标
我需要在大约500K个文档的集合中的每个文档上创建一个新字段Node.js MongoDB-错误:getMore命令失败:找不到光标,node.js,mongodb,mongodb-query,cursor,Node.js,Mongodb,Mongodb Query,Cursor,我需要在大约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.stre
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);
它一开始运行良好,但几小时后,大约10万条记录出现错误:
Error: getMore command failed: {
"ok" : 0,
"errmsg": "Cursor not found, cursor id: ###",
"code": 43,
}: ...
编辑-查询性能:
正如@NeilLunn在他的评论中指出的,您不应该手动筛选文档,而应该使用。查找(…)
:
db.snapshots.find({
roundedDate: { $exists: true },
stream: { $exists: true },
sid: { $exists: false }
})
此外,使用MongoDB 3.2中提供的工具将比单独进行更新更有效
这样,您就有可能在游标的10分钟生命周期内执行查询。如果仍然需要更多时间,则光标将过期,并且您仍将遇到相同的问题,如下所述:
这里发生了什么:
错误:getMore命令失败
可能是由于光标超时,这与两个光标属性有关:
- 超时限制,默认为10分钟: 默认情况下,服务器将在10分钟不活动后自动关闭光标,或者如果客户端耗尽了光标
- 批大小,第一批为101个文档或16MB,后续批为16MB,与文档数量无关(从MongoDB
3.4开始):
和find()
操作的初始批处理大小默认为101个文档。针对结果游标发出的后续操作没有默认的批处理大小,因此它们仅受16MB消息大小的限制aggregate()
可能的解决方案: 我认为有5种可能的方法可以解决这个问题,3种是好的,各有利弊,还有2种是坏的:
添加
collection.find().batchSize(20)
帮助我略微降低了性能。我也遇到了这个问题,但对我来说,它是由MongDB驱动程序中的错误引起的
它发生在npm包mongodb
的3.0.x
版本中,例如在Meteor1.7.0.x
中使用,我也在该版本中记录了这个问题。该评论中对其进行了进一步描述,该线程包含一个确认该错误的示例项目:
将npm包更新为
3.1.x
为我修复了它,因为我已经考虑了@Danziger在这里给出的好建议。当使用Java v3驱动程序时,应该在FindOptions中设置noCursorTimeout
DBCollectionFindOptions options =
new DBCollectionFindOptions()
.maxTime(90, TimeUnit.MINUTES)
.noCursorTimeout(true)
.batchSize(batchSize)
.projection(projectionQuery);
cursor = collection.find(filterQuery, options);
在我的例子中,这是一个负载平衡问题,Node.js服务和Mongos在Kubernetes上作为pod运行时也存在同样的问题。 客户端正在使用默认负载平衡的mongos服务。
将kubernetes服务更改为使用sessionAffinity:ClientIP(粘性)为我解决了问题。我原以为您最大的问题是这一行
doc.stream&&doc.roundedDate&!doc.sid
。您只需传递.find()
,而不使用任何查询表达式,而是过滤代码中的所有文档。让数据库通过移动到查询.find({“roundedDate”:{“$exists”:true},“stream”:{“$exists”:true},“sid”:{“$exists”:false})
来完成这项工作。您还应该每500个左右的项使用一次,而不是在服务器上实际执行每个语句。不要重复不必要的项目,2。使用批量提交以避免服务器确认开销。应该可以节省当前流程的“小时”。您可以进行其他更改以保持光标处于活动状态,但最好的做法是基本上减少处理时间。甚至可以通过\u id
上的“范围”来分解文档的选择,以进一步减少可能的选择。很棒的信息@NeilLunn。我在看bulkWrite()——在我使用bulkWrite()和updateMany()的特殊情况下,问题是我无法访问每个记录,也无法创建sid字段。不过,我一直在寻找这样的方法。我可以使用mongoose在一个数组中本地组装/更新所有记录,但我找不到在数据库中插入/更新/覆盖它们的方法。我想你误解了.bulkWrite()
的用法。举一个例子来看。搞乱光标超时只会让你走这么远。您真正需要做的是减少开销。@NeilLunn感谢您指出这一点,我完全没有注意到这一点,而且实际上有可能在游标过期之前执行查询,特别是如果有索引可以匹配.find(…)
查询。光标未找到
错误是否由超时以外的情况引起?我想很少,但如果您进行快速搜索,您会看到由于驱动程序中的错误而发生过几次:,非常清楚,光标超时是否在下一批检索时重置(即,下次客户收到一批101份文件时)?Thanks@mils是,如果您请求新批次,且光标自上次请求后尚未过期(您仍在10分钟内)