1

更新:这个故事是题外话,标题误导。问题是由损坏的数据集引起的,而不是游标或 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 进行了一些讨论,他帮助我发现了以下内容:

  1. 将 cursor.batchSize() 设置为 10 不起作用。
  2. 该行为不是由等待 10 分钟的空闲游标引起的。游标从服务器快速拉取数据,但仍然无效。
  3. 真正的问题是,在它以这种方式失效后,我再也无法重新分配任何可用的游标了。所有新的游标都拒绝给我数据。有一种可能的解决方法:在这种情况发生之前关闭游标,然后重新分配一个并使用范围查询跳回。

实验正在进行中。实时更新此线程:-)

更新失败!每次读取 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

怎么办?这真的很糟糕。

4

0 回答 0