简体   繁体   English

php脚本中的内存泄漏

[英]memory leak in php script

I have a php script that runs a mysql query, then loops the result, and in that loop also runs several queries: 我有一个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
}

The update query never fails. 更新查询永远不会失败。

For some reason, between the two calls of memory_get_usage , there is some memory added. 出于某种原因,在两次调用memory_get_usage ,添加了一些内存。 Because the big loop runs about 500.000 or more times, in the end it really adds up to alot of memory. 因为大循环运行大约500.000次或更多次,最终它实际上增加了很多内存。 Is there anything I'm missing here? 这里有什么我想念的吗?
could it herhaps be that the memory is not actually added between the two calls, but at another point in the script? 难道这可能是两个调用之间实际上没有添加内存,而是脚本中的另一个点?

Edit: some extra info: Before the loop it's at about 5mb, after the loop about 440mb, and every update query adds about 250 bytes. 编辑:一些额外的信息:循环之前它大约是5mb,循环后大约440mb,每个更新查询增加大约250个字节。 (the rest of the memory gets added at other places in the loop). (其余的内存将添加到循环中的其他位置)。 The reason I didn't post more of the "other stuff" is because its about 300 lines of code. 我没有发布更多“其他东西”的原因是因为它有大约300行代码。 I posted this part because it looks to be where the most memory is added. 我发布了这个部分,因为它看起来是添加最多内存的地方。

This memory leak would only be a problem if it's killing the script with a "memory exhausted" error. 这个内存泄漏只会是一个问题,如果它用“内存耗尽”错误杀死脚本。 PHP will happily garbage collect any unusued objects/variables on its own, but the collector won't kick until it has to - garbage collection can be a very expensive operation. PHP将很乐意自己垃圾收集任何异常的对象/变量,但收集器不会踢,直到它必须 - 垃圾收集可能是一个非常昂贵的操作。

It's normal to see memory usage climb even if you're constantly reusing the same objects/variables - it's not until memory usage exceeds a certain level that the collector will fire up and clean house. 即使您经常重复使用相同的对象/变量,看到内存使用量也会增加是正常的 - 直到内存使用率超过某个级别才会收集到的内容才会启动并清理内部。

I suspect that you could make things run much faster if you batched userIDs into groups and issued fewer updates, changing more records with each. 我怀疑如果你将用户ID分组并发布更少的更新,每次更改更多记录,你可以让事情运行得更快。 eg do the following: 例如,执行以下操作:

UPDATE user_roundscores SET ursUpdDate=NOW() WHERE ursUserTeamIdFK IN (id1, id2, id3, id4, id5, etc...)

instead of doing it one-update-per-user. 而不是每个用户更新一次。 Fewer round-trips through the DB interface layer and more time on the server = faster running. 通过数据库接口层的往返次数减少,服务器上的时间更长=运行速度更快。

As well, consider the impact of now expanding this to millions of users, as you say in a comment. 同样,请考虑现在将其扩展到数百万用户的影响,正如您在评论中所说的那样。 A million individual updates will take a non-trivial amount of time to run, so the NOW() will not be a "constant". 一百万次单独更新将花费非常少的时间来运行,因此NOW()将不会是“常量”。 If it takes 5 minutes to do the full run, then you're going to get a wide variety of ursUpdDate timestamps. 如果完整运行需要5分钟,那么您将获得各种各样的ursUpdDate时间戳。 You may want to consider cacheing a single NOW() call in a server-side variable and issue the updates against that variable: 您可能需要考虑在服务器端变量中缓存单个NOW()调用,并针对该变量发出更新:

 SELECT @cachednow :p NOW();
 UPDATE .... SET ursUpDate = @cachednow WHERE ....;

The best way is probably to get all userIds and flush them to a file. 最好的方法可能是获取所有userIds并将它们刷新到文件中。 Then run a new script which forks with pipes to x amount of worker drones. 然后运行一个新的脚本,用管道分叉x个工作无人机。 Then just give them a small list of userIds to process as they complete each list. 然后给他们一个小的userIds列表,在他们完成每个列表时进行处理。 With multiple cpus/cores/servers you can finish the task faster. 使用多个cpus / cores / servers,您可以更快地完成任务。 If one worker fails, just start a new one. 如果一个工人失败,只需启动一个新工人。 To use other servers as workers you can call them with curl/fopen/soap/etc from a worker thread. 要将其他服务器用作工作程序,可以使用curl / fopen / soap / etc从工作线程调用它们。

I think you should try calling mysql_free_result() at some point during the loop. 我想你应该在循环中的某个时候尝试调用 mysql_free_result() — From the comments: - 来自评论:

It's worth noting that mysql_query() only returns a resource for SELECT , SHOW , EXPLAIN , and DESCRIBE queries. 值得注意的是,mysql_query()仅返回SELECTSHOWEXPLAINDESCRIBE查询的资源。

So there is no result to free for an update query. 因此,没有结果可以释放更新查询。

Anyway, your approach is not the best to begin with. 无论如何,你的方法不是最好的开始。 Try mysqli paramterized statements instead, or (even better) updating the rows at the database directly. 改为尝试使用mysqli paramterized语句,或者(甚至更好)直接更新数据库中的行。 It looks like all of the SQL in the loop could be handled with one single UPDATE statement. 看起来循环中的所有SQL都可以使用一个UPDATE语句来处理。

您可能在每次迭代中看到额外使用的内存的部分原因是PHP还没有(还)垃圾收集不再引用的内容。

From the php.net memory_get_usage manual : php.net memory_get_usage手册

Parameters 参数

real_usage Set this to TRUE to get the real size of memory allocated from system. real_usage将此值设置为TRUE以获取从系统分配的实际内存大小。 If not set or FALSE only the memory used by emalloc() is reported. 如果未设置或FALSE,则仅报告emalloc()使用的内存。

With this parameter set to true, the script showed no increase of memory, like I expected. 将此参数设置为true,脚本显示没有像我预期的那样增加内存。

The unset call is pointless/irrelevant. unset呼叫是无意义/无关紧要的。 Try with mysql_free_result though - It might have some effect. 尝试使用mysql_free_result - 它可能会产生一些影响。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM