MongoDB分片集群更新时报远程shard游标ID不存在错误如何解决
报错根因
MongoDB默认游标在10分钟没有客户端交互时会自动回收,800万条数据遍历处理耗时远超过10分钟,就会触发cursor not found错误。
方案1:直接添加noCursorTimeout配置
在aggregate方法的第二个参数中添加游标配置即可开启无超时,修改后的完整代码如下:
db.getCollection('TEST_Collection').aggregate( [ { $match: { '1List.Comment': { $exists: true } } }, { $project: { '1List.Comment': 1 } } ], // 新增无超时游标配置 { cursor: { noCursorTimeout: true } } ).forEach(function(doc) { // 注意:属性名以数字开头不能直接用点语法,需用[]访问,同时修正正则多余空格问题 const cleanedComment = doc['1List'].Comment.replace(/[^a-zA-Z0-9 ]/g, ''); // _id是唯一键,用updateOne即可,比updateMany性能更高 db.TEST_Collection.updateOne( { "_id": doc._id }, { "$set": { "1List.Comment": cleanedComment } } ); })
开启noCursorTimeout的注意事项:
- 游标不会自动过期,必须确保遍历完整执行,否则会占用服务端资源,需要手动调用
cursor.close()释放 - 原代码中
doc.1List的属性访问方式不符合JavaScript语法,属性名以数字开头时需要用doc['1List']的方式访问,否则会抛出语法错误 - 单条更新时使用
updateOne即可,_id是唯一键,不需要使用updateMany,性能更高
方案2:更稳妥的批量分页处理(无游标超时风险)
如果担心noCursorTimeout占用服务端资源,可以用分页遍历+批量写入的方案,完全避免长游标问题,处理速度也比单条更新快3~10倍:
// 每次处理10000条,可根据服务器性能调整 const batchSize = 10000; // 初始上次处理ID为最小ObjectId let lastId = ObjectId('000000000000000000000000'); let hasMore = true; while (hasMore) { // 查询当前批次的文档 const docs = db.getCollection('TEST_Collection').find( { _id: { $gt: lastId }, '1List.Comment': { $exists: true } }, { '1List.Comment': 1 } ).sort({ _id: 1 }).limit(batchSize).toArray(); if (docs.length === 0) { hasMore = false; break; } // 组装批量更新操作 const bulkOps = docs.map(doc => { const cleanedComment = doc['1List'].Comment.replace(/[^a-zA-Z0-9 ]/g, ''); return { updateOne: { filter: { _id: doc._id }, update: { $set: { '1List.Comment': cleanedComment } } } }; }); // 执行批量更新 db.TEST_Collection.bulkWrite(bulkOps); // 更新最后处理的ID lastId = docs[docs.length - 1]._id; // 可选:每批处理完休眠1秒,避免压垮数据库 sleep(1000); }
内容的提问来源于stack exchange,提问作者Swoop
相关产品推荐
相关产品推荐

