删除时出现光标问题



mongodb没有->remove()->limit()。这就是为什么我用我的小脚本来解决这个任务。

<?php
$conn = new Mongo('127.0.0.1');
$db = $conn->experimentDB;
$experimentCollection = $db->experimentCollection;
foreach($ruleset AS $ruleset_item)
{
    $max_remove_loops=3;
    $max_limit_per_loop=1000;
    MongoCursor::$timeout = 1*60*1000;
    for($remove_loops=0;$remove_loops<$max_remove_loops;$remove_loops++)
    {
        if(!TEST)
            $cursor = $experimentCollection->find($ruleset_item)->limit($max_limit_per_loop);//->skip($remove_loops*$max_limit_per_loop);
        else
            $cursor = $experimentCollection->find($ruleset_item)->limit($max_limit_per_loop)->skip($remove_loops*$max_limit_per_loop);
        $items=0;
        foreach($cursor AS $cursor_item)
        {
            //print_r($cursor_item['_id']);
            print('.');
            if(!TEST)
                $experimentCollection->remove(array('_id' => $cursor_item['_id']));
            $items++;
        }
        if($items==0)
        {
            break;
            print(' that was the last one. DONE ');
        }
        //$cursor->reset();
    }
}
?>

这最终出现在中

致命错误:未捕获异常"MongoCursorTimeoutException",消息为"游标超时(超时:60000,剩余时间:0:0,状态:0)"

这就是为什么我试图用max_remove_loops和max_limit_per_loop来分割任务,并将max_limit_prer_loop更改为1min、1h、2h等。

然而,似乎还有另一个问题,为什么剧本在几百次删除后仍然挂着。有时在200-2000之间。(按打印计数('.'))

这看起来像是一个随机错误,取决于mongodb必须管理的其他任务、RAM和CPU负载。

只是猜测,但如果循环捕捉到的是队列中等待删除的同一个光标,并且有一点延迟,那么它可能会因为删除而引起麻烦?

如何修复此脚本以使其具有容错性并继续而不是挂起?

这里可以进行一些改进。

对于初学者来说,您的脚本只访问每个文档的_id字段。因此,您可以在投影中显式地包括_id字段,并隐式地排除所有其他字段(即MongoCollection::find()的第二个参数)。这在db.collection.find()文档中也有描述。投影有助于限制从服务器发回的数据量。

此外,您应该确保在此脚本中发出的查询已编入索引。当您使用较大的跳过偏移量时,MongoDB首先执行查询,然后逐个遍历结果,直到跳过给定的数字,然后开始返回结果。对于未编制索引的查询,遍历磁盘上的文档可能是一个非常缓慢的过程。对于索引查询,它甚至可能很慢,这取决于跳过的大小。使用limit/skip进行分页的另一种选择是使用范围查询,从大于或小于上次看到的值中提取值。如果你对这种方法感到好奇,我会告诉你最近的答案,其中包括一些关于这个主题的链接。

为了调试查询并确定它们是否被索引,可以使用MongoCursor::explain()。关于其返回值的其他文档(例如如何确定查询是否被索引)可以在cursor.deexplain()文档中找到。

最后,我建议重构您的脚本,在删除任何内容之前,先收集要删除的ID。假设您的ID是12字节的ObjectId(PHP中的MongoId对象),那么在数组中收集它们应该没有问题。这将允许您在没有任何限制/跳过业务的情况下遍历查询的所有结果。之后,您可以发出一系列单个文档删除,也可以使用$in运算符发出一个或多个删除,以一次匹配多个ID。

最新更新