CakePHPで大量のデータをfind(‘all’)したらメモリが足りなくなった【やってみたけど…編】
開発チームの長谷です。
この間試しに、
CakePHPで10万件近くあるデータをfind(‘all’)で取得しようとしたらメモリが足りなくなって、
以下のエラーが出てしまいました。。
Allowed memory size of 134217728 bytes exhausted
そこで、メモリを抑えて10万行近くのデータを取ってこれるようにする方法を色々と調べてみました。
現在のコード
$data = $this->Model->find('all'); // 何らかの処理 .........
query()を使用する
query()で取得する方がfind(‘all’)で取得するよりも処理が軽くなるらしいです。
なので、findで取得してきていたものをquery()で取得するように修正。
$data = $this->Model->query("SELECT * FROM hogehoge;"); // 何らかの処理 .........
結果
気持ち処理が軽くなった気がしましたが、
find('all')して取得した時と変わらず、メモリが足らずエラーが出てしまいました。
query() のキャッシュを無効にする
公式を見たところ、query()にはデフォルトでクエリをキャッシュする仕組みになっているようです。
このクエリをキャッシュするのを無効にするためには、query($query, $cachequeries = false)
というように第2引数に false を指定することで無効にすることができます。
てことで、query() の第2引数に false を指定してみました。
$data = $this->Model->query("SELECT * FROM hogehoge;", $cachequeries = false); // 何らかの処理 .........
結果
結論としては特に変化はありませんでした。。
ループを使ってデータを分割しながら取得する
そもそも上の方法だと、10万件のデータを一気に取得していることには変わりがないので、
メモリが足らなくなるのは当然でした。
ということでlimit,offsetを駆使してループ処理でデータを分割して取得するようにしてみました。
// データの件数を取得する $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); // 何らかの処理 ......... }
結果
素晴らしい結果となりました。
メモリエラーも出ず、データもちゃんと取得できていましたのでこれで問題ないですね!!
てことで、もし同じ現象で困っている方がいらっしゃいましたら参考にしていただければ幸いです。
以上です。
しかし、この方法では…
続きはこちらをご覧ください。
CakePHPで大量のデータをfind(‘all’)したらメモリが足りなくなった【解決】 | 株式会社ビヨンド