MySQLi查询结果:最好的方法,你关闭,免费,两者?

时间:2010-03-10 14:57:12

标签: php mysqli

我对使用MySQLi,查询和相关内存管理有一些疑问。这里的代码只是为了澄清我的问题,所以不要在它上面进行错误检查,等等。我知道需要做的事情:)

假设我有这样的事情:

@ $db = new mysqli($dbhost, $un, $ps, $dbname);
$query = "SELECT field1, field2 ".
         "FROM table1 ".
         "WHERE field1={$some_value}";
$results = $db->query($query);

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

$query = "SELECT field1, field2 ".
         "FROM table2 ".
         "WHERE field1={$some_value2}";
// 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

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

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

  2. 与1相关,当我最后清理时,是否只清理了最后的结果?

  3. 当我尝试清理结果时,我应该如上所述解除它,我应该关闭它,还是两者兼而有之?

  4. 我问问题3,因为mysqli::query的{​​{3}}文档有一个使用close的示例,即使close不是mysqli_result的一部分(参见 {{中的示例1) 3}} 的)。相比之下,我的普通PHP参考文本使用free PHP和MySQL Web开发,第四版,Welling和Thomson)。

5 个答案:

答案 0 :(得分:53)

  

当我分配结果时   对$results的第二次查询,会发生什么   与...相关的记忆   以前的结果?

执行此操作时:

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

如果之前$results中有某些内容,则无法再访问此旧内容,因为没有任何参考内容。

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

这至少适用于一般PHP变量;但是,在SQL查询结果的情况下,某些数据可能会保留在驱动程序级别的内存中 - 而PHP没有太多控制权。


  

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

我从不这样做 - 但是,引用mysqli_result::free的手册页:

  

注意:你应该永远释放你的   结果与mysqli_free_result(),当   您的结果对象不是必需的   再

对于一个小脚本可能无关紧要......唯一可以确定的方法是在调用该方法之前和之后使用memory_get_usage进行测试,以查看是否存在差异


  

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

脚本结束时:

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

因此,在脚本结束时,可能根本不需要释放结果集。


  

当我尝试清理结果时,   我应该如上所述解放它,   我应该关闭它,还是两者兼而有之?

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

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

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

并且“脚本”的结尾意味着“连接将被关闭”即使您没有指定它;我自己几乎从不关闭连接。

答案 1 :(得分:15)

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

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

第二,附加点。正如已经指出的那样,PHP的执行模式意味着最终将清除所有内容,因此,您不一定需要担心释放内存。但是,如果您正在分配大量结果对象,或者您正在分配特别大的结果对象(例如,获取大量数据),那么您应该在完成后释放内存以进一步防止出现问题沿着执行的道路走下去。当您的应用程序开始获得更多流量时,这变得尤为重要,因为跨会话的内存总量很快就会变得非常重要。

答案 2 :(得分:0)

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

$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 :(得分:0)

感谢所有答案,我还想在一个脚本中添加运行多个MySQL查询的经验。在具有多个查询的MySql过程之后运行查询后,Mysqli给出了“命令不同步”错误。为了解决这个问题,我必须使用rizwan-mirza的解决方案将所有开放结果集释放到mysqli_free_result()任意mysqli_more_results() https://stackoverflow.com/a/25907704/462781

答案 4 :(得分:-3)

一般的PHP方法不是关闭任何已打开的资源。一切都将在脚本结束时自动关闭。 情况下,您需要处理手动关闭,如果您要运行 long heavy code ,这对于PHP来说并不常见。