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。