最近在做一个测试,需要把一个庞大的mongodb数据缓慢的取出来进行统计。今天主要记录一下mongodb的游标使用,通过游标来去对数据进行遍历。
实例代码:js语言
1.游标的获取
var cursor = db.collection.find({});
2.游标的使用
打开游标
cursor.hasNext() 判断游标是否已经取到尽头
读取数据
cursor.Next() 取出游标的下一个文档
关闭游标
cursor.close() 此步骤可省略,通常为自动关闭,也可以显示关闭
用while循环来遍历游标示例
while(cursore.hasNext()) {
printjson(mycursor.next());
}
游标生命周期
a、游标完成匹配结果的迭代后,它会清除自身;一般是10分钟的有效时间。
b、缺省情况下,游标在十分钟内没有使用,游标自动关闭或者客户端已经迭代完整个游标;
c、可以通过cursor.noCursorTimeout()来定义游标超时时间-但是非常不推荐使用,这个游标必须要通过代码主动关闭,否则将一直在内存中消耗资源。
如:var myCursor = db.users.find().noCursorTimeout()
e、对于自定义超时时长的游标可以使用cursor.close() 来关闭游标
如:db.collection.find(<query>).close()
3.游标超时处理
游标默认在10分钟内或自动消除,我们将无法遍历,网上看到两种方法。
方法一:
设置用永不超时的游标,但是这个方法还是有弊端的,不能自动销毁游标,必须代码调用close才可以。如果代码或者服务出现异常退出就可能无法释放这个资源,除非重启mongodb了。想想都不合适。
var myCursor = db.users.find().noCursorTimeout()
方法二:
设置batch的大小,大致一次就是每次多取一些游标,还是争取在10分钟内遍历完数据。但是如果数据量比较小,还没有什么问题。如果数据量大,是吧 哈哈。
db.collection.find().batch_size(30):
方法三:重新连接(目前测试代码中使用这种方法)
var id_map = new Map();//存储最新的一条_id
if(cursor.hasNext()){
resultData = cursor.next();
}
resultData .then((data, reject) => {
if(data){
id_map.set("_id",data._id);
return;
}
}).catch(
res => {
console.error('promise error::', res);
if(res.code == 143) {//Cusor already
callback(null);
return;
}
let _id = id_map.get("_id");
global.cursor = db.collections.find({_id:{"$gte":_id}});
});
上面代码的大致思路是:
1.首先我们把每次最新的获取数据的_id通过一个map对象存起来(每次都是刷新操作)
2.当游标失效以后,通过捕获promise的异常,可以拿到这次异常(也可以指定错误码),然后在重新新建游标,添加匹配_id
的条件就可以了。
3.如果想慢慢取出来可以用个定时器,漫游的取就可以了。