我有一个php脚本,它运行mysql查询,然后循环结果,在该循环中还运行几个查询:
$sqlstr = "SELECT * FROM user_pred WHERE uprType != 2 AND uprTurn=$turn ORDER BY uprUserTeamIdFK";
$utmres = mysql_query($sqlstr) or trigger_error($termerror = __FILE__." - ".__LINE__.": ".mysql_error());
while($utmrow = mysql_fetch_array($utmres, MYSQL_ASSOC)) {
// some stuff happens here
// echo memory_get_usage() . " - 1241<br/>\n";
$sqlstr = "UPDATE user_roundscores SET ursUpdDate=NOW(),ursScore=$score WHERE ursUserTeamIdFK=$userteamid";
if(!mysql_query($sqlstr)) {
$err_crit++;
$cLog->WriteLogFile("Failed to UPDATE user_roundscores record for user $userid - teamuserid: $userteamid\n");
echo "Failed to UPDATE user_roundscores record for user $userid - teamuserid: $userteamid<br>\n";
break;
}
unset($sqlstr);
// echo memory_get_usage() . " - 1253<br/>\n";
// some stuff happens here too
}更新查询永远不会失败。
由于某种原因,在memory_get_usage的两次调用之间,增加了一些内存。由于大循环运行了大约500.000次或更多次,最终它实际上增加了大量的内存。我是不是漏掉了什么?
有没有可能内存实际上并没有添加到两个调用之间,而是在脚本的另一个地方?
编辑:一些额外的信息:在循环之前大约是5mb,在循环之后大约是440mb,并且每个更新查询增加大约250字节。(其余的内存被添加到循环中的其他位置)。我没有发布更多“其他东西”的原因是因为它大约有300行代码。我张贴了这个部分,因为它看起来是添加了最多内存的地方。
发布于 2010-04-16 16:30:10
从php.net memory_get_usage manual
参数
real_usage将其设置为TRUE,以获取从系统分配的实际内存大小。如果未设置或设置为FALSE,则只报告emalloc()使用的内存。
当这个参数设置为true时,脚本并没有像我预期的那样增加内存。
发布于 2010-04-09 10:53:07
只有在“内存耗尽”错误杀死脚本的情况下,内存泄漏才会成为问题。PHP会很高兴地自己对任何不常用的对象/变量进行垃圾回收,但收集器不会踢它,直到它不得不这样做-垃圾收集可能是一个非常昂贵的操作。
即使你经常重用相同的对象/变量,也会看到内存使用量攀升,这是很正常的-直到内存使用量超过一定的水平,收集器才会启动并清理房间。
我怀疑,如果您将userID批处理到组中,并发出更少的更新,每次更改更多的记录,那么您可以让事情运行得更快。例如,执行以下操作:
UPDATE user_roundscores SET ursUpdDate=NOW() WHERE ursUserTeamIdFK IN (id1, id2, id3, id4, id5, etc...)而不是为每个用户更新一次。通过DB接口层的往返次数更少,在服务器上的时间更长=运行速度更快。
同样,考虑一下现在将其扩展到数百万用户的影响,正如您在评论中所说的那样。一百万个单独的更新将花费大量的时间来运行,因此NOW()不会是一个“常量”。如果跑完全程需要5分钟,那么你会得到各种各样的ursUpdDate时间戳。您可能需要考虑在服务器端变量中缓存单个NOW()调用,并针对该变量发出更新:
SELECT @cachednow :p NOW();
UPDATE .... SET ursUpDate = @cachednow WHERE ....;发布于 2010-04-08 23:23:14
最好的方法可能是获取所有userIds并将它们刷新到一个文件中。然后运行一个新的脚本,该脚本将管道派生到x数量的工人无人机。然后,在他们完成每个列表时,只需给他们一个小的userIds列表进行处理。使用多个cpus/核心/服务器,您可以更快地完成任务。如果一个工人失败了,只需启动一个新的。要使用其他服务器作为工作线程,可以从工作线程使用curl/fopen/soap/etc调用它们。
https://stackoverflow.com/questions/2600724
复制相似问题