删除时出现游标问题

时间:2013-07-19 12:07:42

标签: php mongodb

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_per_loop更改为1min,1h,2h等。

然而,似乎有另一个问题,为什么脚本在几百个删除后挂起。有时在200-2000之间。 (按打印计算('。'))

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

只是一个猜测,但也许它会导致麻烦,因为如果循环正在捕获队列中的同一个光标,并且稍微延迟删除了吗?

如何修复此脚本以容错并继续而不是挂起?

1 个答案:

答案 0 :(得分:1)

这里可以做一些改进。

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

此外,您应确保将您在此脚本中发出的查询编入索引。当您使用大的跳过偏移时,MongoDB首先执行查询,然后单独遍历结果,直到跳过给定的数字并且它可以开始返回结果。对于未编制索引的查询,这可能是在磁盘上遍历文档的过程非常缓慢。对于索引查询,它甚至可能很慢,具体取决于跳过的大小。使用限制/跳过分页的另一种方法是使用范围查询,从中获取大于或小于上次查看的值。如果您对此方法感到好奇,我会引导您this recent answer,其中包含有关该主题的一些链接。

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

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