[Laravel]EloquentパフォーマンスTips

Laravel Laravel

この記事はデータが大きくなるデータベース向けに、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();
});
PHP

N+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;
}
PHP
N+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

コメント

タイトルとURLをコピーしました