ど素人から毛を生やす。<延>

PDOで大量のデータをSELECTしたらメモリリークになったので、非バッファモードで実行する。

Web > PHP 2020年11月13日(最終更新:5月前)

どもです。

PHP7時代のMySQL利用といえばPDOですが、PDOを用いて大量のデータを取得しようとしたところ、メモリリークが発生しました。

PHP Fatal error: Allowed memory size of xxx bytes exhausted
  • リークはPDOStatement::fetchの部分で発生する。
  • 一度に取得する件数を変更しても、必ず特定のデータ量に達したところでリークする。
  • 変数のメモリ開放を試行したが全く効果なし。

ということで、PDOの実行を非バッファモードで行うことにしました。

 クエリは、デフォルトではバッファモードで実行されます。 つまり、クエリの結果がすぐに MySQL サーバーから PHP に転送され、 PHP プロセスのメモリ内に結果を保持し続けるということです。
(中略)
 バッファクエリを使うのは、 結果セットの量が限られている場合や事前に結果の行数を知りたい場合だけにとどめるべきでしょう。 結果が大量に返ってくることが想定できる場合は、非バッファモードを使わないといけません。
$db = new PDO($DSN, $userName, $password, $option);
$db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);

$res = $db->query($sql);
while($row = $res->fetch()){
	//処理
}

上手くいきました、が、続けてSQLを実行しようところ、以下のエラーが発生。

Fatal error: Call to a member function fetch() on a non-object

非バッファモードでデータを扱う場合、fetch()完了後にcloseCursor()を行わなければならないそうです。

$db = new PDO($DSN, $userName, $password, $option);
$db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);

$res = $db->query($sql);
while($row = $res->fetch()){
	//処理
}
$res->closeCursor();

また、非バッファモードはその特性上、保持できるレスポンス(上記でいう$res)は一度に一つだけなので、while中に別のクエリを実行、などということはできないことを留意です。

この記事は役に立ちましたか?
  • _(:3」∠)_ 面白かった (0)
  • (・∀・) 参考になった (0)
  • (`・ω・´) 役に立った (0)