2

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之间。(按 print('.') 计算)

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

只是一个猜测,但如果循环正在捕获已在队列中等待被删除的相同游标,那么它可能会因为删除而引起麻烦?

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

4

1 回答 1

1

这里可以进行一些改进。

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

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

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

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

于 2013-08-20T20:13:51.710 回答