【问题标题】:Why "Allowed memory size exhausted"?为什么“允许的内存大小已用尽”?
【发布时间】:2012-02-11 18:55:33
【问题描述】:

我正在编写一个批处理脚本并收到 Allowed memory size of 134217728 bytes exhausted 错误。

我不明白为什么内存会被填满。我尝试取消设置 $row 变量,但这并没有改变任何事情。这是我的代码:

// ... (sql connection)
$result = mysql_query("SELECT * FROM large_table");

while ($row = mysql_fetch_array($result)) {
    echo $row['id'] . PHP_EOL;
    unset($row);
}

(简化代码)

为什么内存会填满,我该如何避免?

注意:这是一个批处理脚本。这是正常的,我必须处理这样的数据(经过 100 万行)。

更新:内存不足发生在第 400 000 行左右,所以这一定是循环中的东西?如果可能,我想避免实现分页。

【问题讨论】:

  • 看起来您不应该从该代码 sn-p 中耗尽内存。是否有其他变量在每次迭代中都在增长?
  • mysql查询不会真正返回整个表->意味着结果将计入已用内存?
  • 可能是一个愚蠢的建议,但这是我唯一能想到的:也许你可以尝试刷新你的输出缓冲区?
  • 有趣。由于您使用的是未设置的事实,因此在循环的第一次迭代后内存不应真正增加。我只是好奇如果你使用php.net/manual/en/function.mysql-unbuffered-query.php (mysql_unbuffered_query())会发生什么
  • @Matthieu 内存不足并不意味着问题就在于此。

标签: php memory


【解决方案1】:

尝试使用http://www.php.net/manual/en/function.mysql-unbuffered-query.php (mysql_unbuffered_query()) 来防止整个表被加载到内存中,但仍然避免分页。

【讨论】:

  • 做到了!你的直觉是对的,我喜欢它解决了问题的根源。
  • 顺便说一句,我不明白没有解释的反对票......我赞成补偿
  • 我不确定这是正确的方法,但 +1 因为它确实回答了原始问题:)
  • 这似乎是“正确”的方式,虽然我以前没有听说过。道具
  • 说实话我对这个函数没有任何经验,所以我不知道你以后可能会遇到什么问题,比如 MySQL 连接在处理过程中死掉了。如果阅读本文的人知道任何此类问题,请发布,了解它会很有趣:)
【解决方案2】:

将您的查询限制为例如 1k 个结果并再次执行它(当然使用偏移量),直到您浏览完所有表。 您当前的未设置没有任何区别,因为每次迭代都会覆盖 $row,因此您可以跳过它。

$chunk_size = 1000;
$done = 0;

$keep_asking_for_data = true;
do{
    $result = mysql_query("SELECT * FROM `large_table` LIMIT {$done}, {$chunk_size}");
    $num_rows = mysql_num_rows($result);
    if($num_rows){
        $done += $num_rows;
        while($row = mysql_fetch_assoc($result)){
            echo "{$row['id']}\n";
        }
    } else {
        $keep_asking_for_data = false;
    }
    mysql_free_result($result);
}while($keep_asking_for_data);

刚在我头上编译,希望它有效=D

【讨论】:

  • 我尝试添加 unset 以强制收集变量使用的内存,否则无法保证(PHP GC)。无论如何,请参阅对其他答案的评论和问题更新
  • 该死! “如果可能的话,我想避免实现分页。”当我编写代码时,它不存在:D
  • @PalmTree 是的,对更新感到抱歉;),+1 的努力。如果没有 KillerX 的解决方案,我会很高兴现在工作已经完成;)
  • 支持比我发布的更“传统”的解决方案。
【解决方案3】:

如果您使用的是 MySQL,请对结果进行分页,以免耗尽可用内存。 MySQL 本身正在使用您的数据库结果集占用此内存。查看以下链接,尤其是SELECTLIMIT offset, limit 语法:

http://dev.mysql.com/doc/refman/5.0/en/select.html

【讨论】:

  • 这是一个想法,但我承认我太懒了:p。无论如何,错误发生在第 400 000 行附近(请参阅问题更新),所以它可能是循环中的某些东西,也许我可以修复?
  • 您的方法应该 缓冲一些结果 - 删除缓冲以使分页变得不必要,imo 可能不是这样做的方法。无论如何,现在应该很容易了 - @PalmTree 已经为你做到了:)
  • 为什么你认为删除分页不是好方法?
  • 不,我认为应该添加分页;它正在消除我认为不是正确方法的缓冲。删除缓冲(可能)会降低其性能 - 如果出于良好的技术原因,这很好,但太懒不是其中之一 ;-)
  • 会发布您答案的“副本”,但我只能用代码行说话:D
【解决方案4】:

我在大型数据库中遇到了同样的问题。尽管在大约 400,000 条记录处取消设置 $row 变量,但内存不足,但无缓冲查询修复了它。

仅供其他人参考(以及我再做一次!),一些无缓冲的查询示例代码是:

$sql = "SELECT SEQ, RECTYPE, ROSTERGRP, EMPNM, EMPNUM, DT, RDUTYCAT, ADUTYCAT FROM " .
        $tblRosters . " ORDER BY EMPNUM,DT";
$result  = mysql_unbuffered_query( $sql, $dbConn );
$svRow = array();
while ( $row = mysql_fetch_array( $result ) )
    {
         // your processing code here
    }
    // Unset, close db etc. if you are finished goes here.

【讨论】:

    猜你喜欢
    • 2013-02-08
    • 2014-09-05
    • 2012-04-30
    • 1970-01-01
    • 1970-01-01
    • 2012-08-29
    • 2020-10-06
    • 2018-02-24
    • 2012-08-08
    相关资源
    最近更新 更多