【问题标题】:MySQLi query results: When do I close, free, or both?MySQLi 查询结果:我什么时候关闭、空闲或两者都关闭?
【发布时间】:2011-01-25 22:42:41
【问题描述】:

我有一些关于使用 MySQLi 查询和相关内存管理的问题。

假设我有这样的事情:

$db = new mysqli($dbhost, $un, $ps, $dbname);

$query = "SELECT field1, field2  FROM table1 ";
$results = $db->query($query);

while ($result = $results->fetch_object()) {
    // Do something with the results
}

$query = "SELECT field1, field2 FROM table2 ";
// question 1
$results = $db->query($query);

while ($result = $results->fetch_object()) {
    // Do something with the second set of results
}

// Tidy up, question 2
if ($results) {
    $results->free();
}
if ($db) {
    $db->close();
}

// Question 3, a general one

所以,根据上面代码中的 cmets,这是我的问题:

  1. 当我将第二个查询的结果分配给$results 时,与之前的结果相关联的内存会发生什么变化?我应该在分配新结果之前释放该结果吗?

  2. 与1有关,当我最后清理时,只清理最后的结果就够了吗?

  3. 当我尝试清理结果时,我应该像上面那样释放它,我应该关闭它,还是两者兼而有之?

我问问题 3 是因为 the PHP documentation for mysqli::query 有一个使用 close 的示例,即使 close 不是 mysqli_result 的一部分(参见上面链接中的示例 1)。相比之下,我的普通 PHP 参考文本使用 freePHP 和 MySQL Web 开发,第四版,Welling 和 Thomson)。

【问题讨论】:

    标签: php mysqli


    【解决方案1】:

    当我分配结果时 第二次查询$results,会发生什么 到与 以前的结果?

    当你执行这个时:

    $results = $db->query($query);
    

    如果之前$results 中存在某些内容,则无法再访问此旧内容,因为它已没有任何引用。

    在这种情况下,PHP 会将变量的旧内容标记为“不再需要”——当 PHP 需要一些内存时,它将从内存中删除。

    这至少对于一般 PHP 变量是正确的;但是,对于 SQL 查询的结果,一些数据可能会保存在驱动程序级别的内存中——PHP 对此没有太多控制权。


    我应该在之前释放那个结果吗 分配新的?

    我从不这样做。


    与 1 相关,当我在 结束,正在清理只是最后一次 结果够了吗?

    脚本结束时:

    • 与数据库的连接将关闭 -- 这意味着应该释放驱动程序可能使用的所有内存
    • PHP 脚本使用的所有变量都将被销毁——这意味着它们正在使用的内存应该被释放。

    因此,在脚本结束时,确实不需要释放结果集。


    当我尝试清理结果时, 我应该像上面那样释放它, 我应该关闭它,还是两者都关闭?

    如果您关闭与数据库的连接(按照您的建议使用mysqli::close,这将断开您与数据库的连接。

    这意味着如果您想进行其他查询,则必须重新连接!这一点都不好(需要一些时间,资源,...)

    一般来说,在我确定不再需要它之前,我不会关闭与数据库的连接——这意味着我不会在脚本结束之前断开连接。

    而“脚本结束”的意思是“连接将被关闭”,即使你没有指定它;我几乎从不自己关闭连接。

    【讨论】:

    • 哇!这是一个出色且写得很好且内容丰富的答案。正是我想要的。谢谢!我想总而言之,我过度关注一种处理垃圾收集的语言。再次感谢!
    • @PascalMARTIN,那么当脚本结束时,驱动程序中未清理的资源会发生什么?它们会自动清理吗?
    • 而“脚本结束”的意思是“连接将被关闭”,即使你没有指定它;我自己几乎从不关闭连接。但前提是连接没有建立永久连接,这可以在 mysqli 中使用前缀 p 完成:@see php.net/manual/function.mysql-pconnect
    • 还需要提到的是,当PHP关闭数据库连接时,MySQL服务器会决定是从内存中移除该连接还是将其标记为空闲。这样做的原因是MySQL默认保持一定数量的连接打开,以便在连接请求到达时可以直接使用它们,这当然会提高性能,因为在内存中创建连接自然需要额外的时间。跨度>
    【解决方案2】:

    尽管内存泄漏很少见,但在我看来,发现和纠正内存泄漏是一场噩梦。我特意避开他们。以下是我使用的模式,基于您提供的代码:

    $db = NULL;
    try {
        $dbPool = "p:$dbhost"; // question 3: use pooling
        $db = new mysqli($dbPool, $un, $ps, $dbname);
        if ($db->connect_errno) {
            throw new Exception('' . $db->connect_error . ' ' . $db->connect_errno 
                    . "\n" . $un . '@' . $dbhost . ' ' . $dbname);
            // NOTE: It's commonly considered a security 
            // risk to output connection information e.g.
            // host, user and database names.
        }
    
        $query = "SELECT field1, field2 ".
                 "FROM table1 ".
                 "WHERE field1={$some_value}";
    
        $results = NULL;
        try {
    
            if (!$results = $db->query($query)) {
                throw new Exception($db->error . " " . $db->errno 
                        . "\n" . $query);
                // NOTE: It's commonly considered a security 
                // risk to output SQL ($query).
            }
            while ($result = $results->fetch_object()) {
                // Do something with the results
            }
    
        } catch (Exception $ex) {
            // log, report, or otherwise handle the error
        }
        if ($results) {
            $results->free(); // question 1: why risk it?
        }
    
        $query = "SELECT field1, field2 ".
                 "FROM table2 ".
                 "WHERE field1={$some_value2}";
    
        $results = NULL; 
        try {
    
            if (!$results = $db->query($query)) {
                throw new Exception($db->error . " " . $db->errno 
                        . "\n" . $query);
                // NOTE: It's commonly considered a security 
                // risk to output SQL ($query).
            }            
            while ($result = $results->fetch_object()) {
                // Do something with the second set of results
            }
    
        } catch (Exception $ex) {
            // log, report, or otherwise handle the error
        }
        if ($results) {
            $results->free(); // question 2: again, why risk it?
        }
    
    } catch (Exception $ex) {
        // log, report, or otherwise handle the error
    }
    if ($db) {
        $db->close();
    }
    

    在我看来,连接池会增加内存泄漏的机会,但根据手册,连接池库会自动为您做很多清理工作:

    然而,mysqli 扩展的持久连接提供 内置清理处理代码。 mysqli进行的清理 包括:

    回滚活动交易

    关闭并删除临时表

    解锁表

    重置会话变量

    关闭准备好的语句(总是发生在 PHP 中)

    关闭处理程序

    释放使用 GET_LOCK() 获取的锁

    这确保持久连接在 在客户端进程使用它们之前从连接池返回。

    来源: http://php.net/manual/en/mysqli.persistconns.php

    我也同意 Pascal MARTIN 的观点,即在脚本开头打开连接并在结尾关闭它是个好主意。我认为连接池使这一点变得不那么重要了,但仍然是个好主意。

    【讨论】:

      【解决方案3】:

      一般的 PHP 方式是不关闭任何打开的资源。一切都会在脚本结束时自动关闭。 唯一需要手动关闭的情况是如果你有冗长的代码要运行,这对于 PHP 来说并不常见。

      【讨论】:

      • 谢谢,这很简单。但是,为什么该语言包含 free 作为 mysqli_result 的一部分?这是遗留的东西吗?
      【解决方案4】:

      已经提供的答案很好,但我想补充一点并澄清另一点。

      首先,澄清。关于 close() 方法的使用,重要的是要注意 OP 引用的是 mysqli_result 类的 close() 方法,而不是 mysqli 类。在结果类中,close() 方法只是 free() 方法的别名,如documentation 所示,而在 mysqli 类中,它关闭连接。因此,如果需要,可以在结果上使用 close() 代替 free()。

      第二,附加点。正如已经指出的那样,PHP 的执行模式意味着一切最终都会在您身后清理干净,因此您不必担心释放内存。但是,如果您分配了很多结果对象,或者您分配了特别大的结果对象(例如,获取大量数据),那么您可能应该在完成后释放内存以防止进一步出现问题沿着执行的路径。当您的应用程序开始获得更多流量时,这一点变得尤为重要,其中跨会话占用的内存总量很快就会变得很重要。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-08-28
        • 1970-01-01
        • 2023-03-27
        • 2010-10-17
        • 1970-01-01
        • 1970-01-01
        • 2016-01-21
        相关资源
        最近更新 更多