MongoDB:无法使用游标迭代所有数据(因为数据已损坏)

时间:2013-10-29 04:57:30

标签: javascript mongodb

更新: 故事偏离主题,标题误导性。问题是由损坏的数据集,而不是游标或MongoDB本身引起的。但我宁愿把这个帖子留在这里而不是删除它,因为它可能会帮助其他绝望的人。

===原创故事从这里开始===

一切都从这里开始:MongoDB: cannot use a cursor to iterate through all the data

我试图在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进行了一些讨论,他帮我发现了以下内容:

  1. 将cursor.batchSize()设置为10不起作用。
  2. 行为不是由等待10分钟的空闲光标引起的。光标从服务器快速提取数据,但仍然无效。
  3. 真正的问题是,在以这种方式失效之后,我再也无法重新分配任何可用的游标。所有新游标都拒绝向我提供数据。有一种可能的解决方法:在此之前关闭光标,然后重新分配一个并使用范围查询跳回。
  4. 实验正在进行中。实时更新此主题: - )

    更新:失败!我每次阅读50k记录后都尝试更新光标。它也被困在这个神奇的索引21800000!这非常接近我的cursor.skip()失败偏移!

    更新

    确认猜测:

    c = db.tweets.find().skip(21800000); //works
    c = db.tweets.find().skip(21850000); //doesn't work
    

    我会尝试在这个范围内进行二元搜索以找到幻数。

    更新

    好的......找到了幻数。

    db.tweets.find()。itcount() - &GT; 21837006

    db.tweets.find()。COUNT() - &GT; 253531208

    现在怎样?这真的很糟糕。

0 个答案:

没有答案