この記事はデータが大きくなるデータベース向けに、Eloquentのパフォーマンスを向上させる方法を紹介します。
カラムの指定
SQLを発行する際にカラムを指定を指定して、余計なデータを抽出しないようにします。
Eloquentだけでなく、実際にSQLで抽出する際にもよく利用される手法です。
$users = User::select('name', 'email')->get();
PHPインデックス作成
こちらも通常のDBチューニングでよく利用される手法です。
テーブル作成後でもインデックスやユニーク属性を付けられますが、既存のデータの状態によっては、ユニーク属性が付けられなかったりするので、重くなりそうなテーブルの場合は、先に作成する方が望ましいです。
Schema::create('users', function (Blueprint $table) {
$table->string('email')->unique();
$table->string('date')->index();
});
PHPN+1問題を軽減するためのEager Loading
関連するデータに対して複数のクエリを発行せず、1クエリだけで必要なデータを全て取得できます。
N+1問題とは、N人のユーザーの投稿データを取得しようとした場合、N人のユーザーを取得するクエリと、投稿したデータを取得するクエリをN回発行することになります。これがN+1問題というものです。(以下参照)
Eager Loadingなし
$users = User::whereIn(range(1, 10))->get();
foreach ($users as $user) {
$posts = $user->posts;
}
PHPN+1問題で発行されるクエリ
SELECT * FROM `users` WHERE id IN (1, 2, ....., 10);
SELECT * FROM `posts` WHERE `user_id` = 1;
SELECT * FROM `posts` WHERE `user_id` = 2;
:
SELECT * FROM `posts` WHERE `user_id` = 10;
SQL上記のように対象のレコードが増えれば増えるほど、クエリを発行してしまいます。
これの対策としてEager Loading(withメソッド)を利用して取得すると計2クエリで済みます。
N+1クエリの問題を軽減するEager Loadingを利用
$users = User::with('post')->whereIn(range(1, 10))->get();
foreach ($users as $user) {
$posts = $user->posts;
}
PHP発行されるクエリ
SELECT * FROM `users` WHERE `id` IN (1, 2, ....., 10);
SELECT * FROM `posts` WHERE `user_id` IN (1, 2, ....., 10);
SQLクエリのキャッシュ
キャッシュを使用して、頻繁に使用されるクエリの結果を保存し、データベースへの不必要なログインを避けます。
$users = Cache::remember('all_users', $minutes, function () {
return User::all();
});
PHP小分けにして取得
chunk メソッドは Eloquent モデルのサブセットを取得し、それを処理のためにクロージャーに渡します。一度に現在の Eloquent モデルのチャンクのみが取得されるため、多数のモデルを操作する場合、チャンク メソッドによりメモリ使用量が大幅に削減されます。
use App\Models\Flight;
use Illuminate\Database\Eloquent\Collection;
Flight::chunk(200, function (Collection $flights) {
foreach ($flights as $flight) {
// ...
}
});
PHP
コメント