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负载。
只是一个猜测,但也许它会导致麻烦,因为如果循环正在捕获队列中的同一个光标,并且稍微延迟删除了吗?
如何修复此脚本以容错并继续而不是挂起?
答案 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。