更新:这个故事是题外话,标题误导。问题是由损坏的数据集引起的,而不是游标或 MongoDB 本身。但我宁愿把这个帖子留在这里而不是删除它,因为它可能会帮助其他绝望的人。
=== 原创故事从这里开始 ===
一切从这里开始:MongoDB:不能使用游标遍历所有数据
我试图遍历 Java 中的游标,但它失败了,因为我的集合有太多记录(~250M)。我尝试分配一个新游标并使用 cursor.skip 在游标超时但 cursor.skip 本身超时时跳回。
@mnemosyn 为我指出了正确的方法:将工作分为两个阶段:在第一阶段,使用投影光标仅拉出单调的 _id 记录。记录 _id,然后将其作为“检查点”存储在其他地方。在第二阶段,我可以访问任何记录块作为记录的检查点。
所以我写了一个这样的javascript:
db=connect("localhost/twitter");
db.jobScheduler.drop();
for(var i = 0;i<16;++i)
{
db.jobScheduler.save({_id:"s"+i,jobs:[]});
}
var c = db.tweets.find({},{_id:1}).sort({_id:1});
var totalCount = c.count();
var currentBatchSize = 0;
var currentNum = 0;
var currentShard = 0;
var startTid = 0;
var endTid = 0;
var currentTid = 0;
while(true)
{
while(c.hasNext())
{
var doc = c.next()
currentTid = doc._id;
if(currentBatchSize == 0)
{
startTid = doc._id;
}
++currentNum;
++currentBatchSize;
if(currentBatchSize == 50000)
{
currentBatchSize = 0;
endTid = doc._id;
db.jobScheduler.update(
{_id:"s"+currentShard},
{$push:{jobs:[startTid,endTid]}});
currentShard = (currentShard+1)%16;
print(currentNum+"/"+totalCount+"("+currentNum*100/totalCount+"%)");
print("["+startTid+","+endTid+"]");
}
}
if(currentNum != totalCount){
var c = db.tweets.find({_id:{$gt:currentTid}},{_id:1}).sort({_id:1});
print("Cursor resetted....");
}else
break;
}
if(currentBatchSize != 0)
{
currentBatchSize = 0;
endTid = doc._id;
db.jobScheduler.update(
{_id:"s"+currentShard},
{$push:{jobs:[startTid,endTid]}});
currentShard = (currentShard+1)%16;
}
考虑到简单地拉 _id 仍然会导致超时,我添加了一个这样的守卫:
if(currentNum != totalCount){
var c = db.tweets.find({_id:{$gt:currentTid}},{_id:1}).sort({_id:1});
print("Cursor resetted....");
}else
break;
因为当光标超时时,我没有得到异常而是错误的 cursor.hasNext()。因为我在遍历它们时已经记录了 currentTid var c = db.tweets.find({_id:{$gt:currentTid}},{_id:1}).sort({_id:1});
,所以理论上使用范围查询会让我回到原来的位置。然而可怜的小程序最终是这样的:
[337242463750201340,345999466677010400]
21800000/253531208(8.598546968624076%)
[345999469818544100,346244305876295700]
Cursor resetted....
Cursor resetted....
Cursor resetted....
它似乎永远停留在第一次出现光标超时时。范围查询并没有让我回来。
现在我真的很困惑。迭代不起作用。cursor.skip() 不起作用。范围查询不起作用。什么真正适用于 MongoDB?还是我做错了什么?
任何帮助将非常感激!
更新:
我与@AsyaKamsky 进行了一些讨论,他帮助我发现了以下内容:
- 将 cursor.batchSize() 设置为 10 不起作用。
- 该行为不是由等待 10 分钟的空闲游标引起的。游标从服务器快速拉取数据,但仍然无效。
- 真正的问题是,在它以这种方式失效后,我再也无法重新分配任何可用的游标了。所有新的游标都拒绝给我数据。有一种可能的解决方法:在这种情况发生之前关闭游标,然后重新分配一个并使用范围查询跳回。
实验正在进行中。实时更新此线程:-)
更新失败!每次读取 50k 条记录后,我尝试更新光标。它也被困在这个神奇的指数21800000!这非常接近我的 cursor.skip() 失败偏移量!
更新:
证实了猜测:
c = db.tweets.find().skip(21800000); //works
c = db.tweets.find().skip(21850000); //doesn't work
我将尝试在这个范围内进行二分搜索以找到幻数。
更新:
好的...找到幻数。
db.tweets.find().itcount()->21837006
db.tweets.find().count() -> 253531208
怎么办?这真的很糟糕。