CakePHPで大量のデータをfind(‘all’)したらメモリが足りなくなった【解決】
開発チームの長谷です。
前回の記事で、CakePHPでメモリエラーを発生させずに大量のデータをfind(‘all’)する方法を書かせていただきましたが、
その記事の内容で色々と社内でご指摘を受けましたので、
今回は前回の内容のフィードバックを書かせていただきます。
ですので、前回の記事はペーペープログラマーの戯言としてご覧になられると幸いでございます。。。
現在のコード
$data = $this->Model->find('all'); // 何らかの処理 .........
query()を使用するのは×
前回の記事より
query()で取得する方がfind(‘all’)で取得するよりも処理が軽くなるらしいです。
なので、findで取得してきていたものをquery()で取得するように修正。
これは間違いです。
そもそも、queryメソッドは複雑なクエリを実行するときに使うものであって、
使う場合はサニタイズとかバリデーションする必要があるので、安易に使わない方が良いらしいです。
そもそも、findを使うようにした方がフレームワークの機能を有効に使えて楽なので、
queryメソッドを使う必要は今回は全くありませんでした。。
for文でループするのではなく
while文でループしながらデータを取得する方が偉い
前回はforループを使ってデータを分割して取得してました。
前回のコード
// データの件数を取得する $count = $this->Model->find('count'); // 5000件ずつデータを取得するようにする $limit = 5000; // ループする回数({データ件数 ÷ 1回の取得件数}の端数を切り上げた数) $loop = ceil($count / $limit); for ($i = 0; $i < $loop; $i++){ // オフセット $offset = $limit * $i; $data = $this->Model->query("select * from hogehoge as limit {$limit} offset {$offset};", $cachequeries = false); // 何らかの処理 ......... }
この方法でも間違ってはいないのですが、
実はwhile文を使って取得する方が方法として偉いということが分かりました。
while文だとcountを取る必要もないし、loop回数を計算する必要もありません。
あとquery()ではなくfind('all')ですね。。。
てことでwhile文で書き直してみました。
書き直したコード
// 5000件ずつデータを取得するようにする $limit = 5000; $params = array('limit' => $limit, 'offset' => 0); while ($data = $this->Model->find('all', $params)){ // 何らかの処理 $params['offset']+= $limit; }
こちらの方が無駄な処理がないので、for文を使うよりも断然偉いですよね。
勉強になりました!
以上です。
この記事がお役に立てば【 いいね 】のご協力をお願いいたします!