この記事では主に、PHP で MySQL にクエリを実行して多数の結果を返すときのメモリ使用量の問題について説明し、MySQL C API の使用についても説明します。
昨日、同僚が PHP ディスカッション グループで、彼が取り組んでいるプロジェクトが MySQL クエリから多すぎる結果 (最大 100,000) を返し、その結果 PHP メモリが不足したと述べました。 そこで彼は、返された MySQL の結果を反復処理する次のコードを実行する前に、データがすでにメモリ内にあるのか、と尋ねました。
while ($row = mysql_fetch_assoc($result)) {//…
}
もちろん、この問題には多くの最適化方法があります。 ただし、この問題に関して私が最初に考えたのは、MySQL は古典的な C/S (クライアント/サーバー、クライアント/サーバー) モデルであり、結果セットをスキャンする前に、基盤となる実装がネットワーク経由ですべてのデータを送信している可能性があるということです (前提として)。 TCP/IP) を使用してクライアントのバッファを読み取る場合、別の可能性があります。つまり、データがまだサーバーの送信バッファ内にあり、クライアントに渡されていないということです。
PHP と MySQL のソース コードを見る前に、PHP マニュアルに同様の機能を持つ 2 つの関数があることに気付きました。
mysql_query()
mysql_unbuffered_query()
2 つの関数の文字通りの意味と説明により、前者の関数が実行されると、すべての結果セットがサーバーからクライアントのバッファーに読み取られますが、後者の関数は「バッファリングされない」ことを意味します。
つまり、mysql_unbuffered_query() を使用して大きな結果セットを返す SQL ステートメントを実行する場合、結果を走査する前に PHP のメモリが結果セットによって占有されることはありません。 mysql_query() を使用して同じステートメントを実行すると、関数が戻るときに PHP のメモリ使用量が急激に増加し、すぐにメモリが消費されてしまいます。
PHP の関連コードを読むと、これら 2 つの関数の実装の類似点と相違点がわかります。
/* {{{ proto resource mysql_query(string query [, int link_identifier])
SQL クエリを MySQL に送信します */
PHP_FUNCTION(mysql_query)
{
php_mysql_do_query(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQL_STORE_RESULT);
}
/* }}} */
/* {{{ proto resource mysql_unbuffered_query(string query [, int link_identifier])
結果行のフェッチやバッファリングを行わずに、SQL クエリを MySQL に送信します */
PHP_FUNCTION(mysql_unbuffered_query)
{
php_mysql_do_query(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQL_USE_RESULT);
}
/* }}} */
どちらの関数も php_mysql_do_query() を呼び出しますが、2 番目のパラメーター MYSQL_STORE_RESULT と MYSQL_USE_RESULT だけが異なります。 php_mysql_do_query() の実装を見てみましょう。
if(use_store == MYSQL_USE_RESULT) {
mysql_result=mysql_use_result(&mysql->conn);
} else {
mysql_result=mysql_store_result(&mysql->conn);
}
Mysql_use_result() と mysql_store_result() は MySQL の C API 関数です。これら 2 つの C API 関数の違いは、後者は結果セット全体を MySQL サーバーからクライアントに読み取るのに対し、前者は結果のメタ情報のみを読み取ることです。セット。
PHP に戻り、mysql_unbuffered_query() を使用して即時のメモリ占有を回避します。 走査プロセス中に結果が「PHP キャッシュ」されない場合 (結果を配列に配置する場合など)、実行プロセス全体が 10 万または 100 万以上のデータを処理するにもかかわらず、PHP が占有するメモリは常に非常に小さい。