web-dev-qa-db-ja.com

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. 2番目のクエリの結果を$resultsに割り当てると、以前の結果に関連付けられたメモリはどうなりますか?新しい結果を割り当てる前に、その結​​果を解放する必要がありますか?

  2. 1に関連して、最後にクリーンアップを行うと、最後の結果だけで十分ですか?

  3. 結果をクリーンアップしようとした場合、上記のように解放する必要がありますか、それとも閉じるべきですか、またはその両方ですか?

[〜#〜] php [〜#〜]mysqli::queryのドキュメントには、closeがmysqli_resultの一部ではない場合でも、closeを使用する例があるため、3番目の質問をします(mysqli :: queryの例1を参照してください)。そして対照的に、私の通常のPHP参照テキストはfreeを使用します(PHPとMySQL Web開発)、第4版、ウェリングおよびトムソン)。

36
Carvell Fenton

2番目のクエリの結果を$resultsに割り当てると、以前の結果に関連付けられたメモリはどうなりますか?

これを実行すると:

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

以前に$resultsに何かがあった場合、参照が残っていないため、この古いコンテンツにはアクセスできません。

このような場合、PHPは、変数の古い内容を「不要になりました」としてマークします-- PHPにメモリが必要になると、メモリから削除されます。

これは、少なくとも、一般的なPHP変数に当てはまります。ただし、SQLクエリの結果の場合、一部のデータはドライバレベルでメモリに保持される可能性があります。 PHPはあまり制御できません。


新しい結果を割り当てる前に、その結​​果を解放する必要がありますか?

私はそれを決してしません-しかし、 mysqli_result::free のマニュアルページを引用します:

注:結果オブジェクトが不要になった場合は、常にmysqli_free_result()を使用して結果を解放する必要があります。

小さなスクリプトではおそらく問題ではありません...そして、確実にする唯一の方法は、そのメソッドを呼び出す前後に memory_get_usage を使用してテストすることです。違いかどうか。


1に関連して、最後にクリーンアップを行うと、最後の結果だけで十分ですか?

スクリプトが終了すると:

  • データベースへの接続は閉じられます-つまり、ドライバーが使用する可能性のあるメモリは解放する必要があります
  • PHPスクリプトによって使用されるすべての変数は破棄されます-つまり、それらが使用していたメモリを解放する必要があります。

したがって、スクリプトの最後では、結果セットを解放する必要はおそらくありません。


結果をクリーンアップしようとした場合、上記のように解放する必要がありますか、それとも閉じるべきですか、またはその両方ですか?

データベースへの接続を閉じると(提案したように mysqli::close を使用して)、これにより接続が切断されますデータベース。

つまり、別のクエリを実行する場合は、再接続する必要があります。これはまったく良くありません(時間がかかり、リソースがかかります...)

一般的に言って、データベースが不要になると本当に確信するまで、データベースへの接続を閉じません。つまり、スクリプトが終了する前に接続を切断しません。

そして、「スクリプトの終わり」は「接続が閉じられることを意味するので "指定しなくても、自分で接続を閉じることはほとんどありません。

56
Pascal MARTIN

すでに提供されている答えは良いですが、私は1つのポイントを追加し、別のポイントを明確にしたいと思いました。

まず、明確化。 close()メソッドの使用に関しては、OPがmysqliクラスではなくmysqli_resultクラスのclose()メソッドを参照していたことに注意することが重要です。結果クラスでは、close()メソッドは documentation に示すようにfree()メソッドの単なるエイリアスですが、mysqliクラスでは接続を閉じます。したがって、必要に応じて、free()の代わりに結果に対してclose()を使用してもかまいません。

第二に、追加のポイント。すでに指摘したように、PHPの実行パターンは、すべてが最終的に背後でクリーンアップされることを意味します。したがって、必ずしもメモリの解放について心配する必要はありません。ただし、多くの結果オブジェクトを割り当てている場合、または特に大きな結果オブジェクトを割り当てている場合(たとえば、大量のデータをフェッチする場合)、問題が発生しないように、完了したらメモリを解放する必要があります。実行のパスをダウン。これは、アプリケーションがより多くのトラフィックを取得し始めるときに特に重要になり、セッション全体で占有されているメモリの総量がすぐに大量になる可能性があります。

15
mr. w

すべての回答に感謝します。1つのスクリプトで複数のMySQLクエリを実行した経験も追加したいと思います。 Mysqliは、複数のクエリを使用するMySqlプロシージャの後にクエリを実行した後、「コマンドが同期していません」というエラーを出しました。これを解決するには、_ rizwan-mirzamysqli_free_result() any mysqli_more_results()へのソリューションを使用して、開いているすべての結果セットを解放する必要がありました https:// stackoverflow.com/a/25907704/462781

0
Pete

まれですが、私の考えでは、メモリリークは発見して修正するのが悪夢です。私は彼らを避けるために道を外れます。以下は、あなたが提供したコードに基づいて、私が使用するパターンです:

$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

また、スクリプトの最初に接続を開き、最後に接続を閉じることをお勧めします。接続プーリングはそれほど重要ではないと思いますが、それでも良い考えです。

0
Jonathan