【大阪 / 横浜】インフラ / サーバーサイドエンジニア募集中!

【大阪 / 横浜】インフラ / サーバーサイドエンジニア募集中!

【導入実績 500社以上】AWS 構築・運用保守・監視サービス

【導入実績 500社以上】AWS 構築・運用保守・監視サービス

【圧倒的 低コスト】Oracle Cloud 構築・運用保守・監視サービス

【圧倒的 低コスト】Oracle Cloud 構築・運用保守・監視サービス

【WordPress専用】高速 クラウド / サーバー『WebSpeed』

【WordPress専用】高速 クラウド / サーバー『WebSpeed』

【低コスト】Wasabi オブジェクトストレージ 構築・運用サービス

【低コスト】Wasabi オブジェクトストレージ 構築・運用サービス

【次世代】ゲーム専用データ分析エンジン『ThinkingEngine』

【次世代】ゲーム専用データ分析エンジン『ThinkingEngine』

【100URLの登録が0円】Webサイト監視サービス『Appmill』

【100URLの登録が0円】Webサイト監視サービス『Appmill』

【SNSアプリ開発】LINE カスタムアプリ開発サービス

【SNSアプリ開発】LINE カスタムアプリ開発サービス

【ECアプリ開発】Shopify カスタムアプリ開発サービス

【ECアプリ開発】Shopify カスタムアプリ開発サービス

【音声アプリ開発】Twilio カスタムアプリ開発サービス

【音声アプリ開発】Twilio カスタムアプリ開発サービス

【グローバル対応】北米リージョン・クラウド / サーバー サポート

【グローバル対応】北米リージョン・クラウド / サーバー サポート

【取材記事】サーバーサイド・バックエンドエンジニアを募集中

【取材記事】サーバーサイド・バックエンドエンジニアを募集中

【YouTube】ビヨンド公式チャンネル「びよまるチャンネル」

【YouTube】ビヨンド公式チャンネル「びよまるチャンネル」

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文を使うよりも断然偉いですよね。
勉強になりました!

以上です。

この記事がお役に立てば【 いいね 】のご協力をお願いいたします!
2
読み込み中...
2 票, 平均: 1.00 / 12
3,382
facebook twitter はてなブックマーク
【大阪 / 横浜】インフラエンジニア / サーバーサイドエンジニア 積極採用中!

【大阪 / 横浜】インフラエンジニア / サーバーサイドエンジニア 積極採用中!

この記事をかいた人

About the author

長谷竜弥

新卒にて株式会社ビヨンドに入社。

Webシステム開発(Webサービス・デジタルコンテンツ・業務管理システム などのブラウザで動くサービス、システムの開発)や、ゲームAPI(アプリゲームとの通信部分のプログラム開発)を行っている。

また、Shopify のプライベート / カスタムアプリの開発も行っている。

元々は大阪オフィスに勤めていたが、2019年に横浜オフィスに転勤。
趣味は野球 / カラオケ / アニメ