こんにちは!
今回はデータベースクエリのパフォーマンスで度々問題となる、「N+1問題」の解消方法についてご紹介していきたいと思います。
本記事では、特にLaravelでの解消方法に絞って説明していきます。
「N+1問題によってパフォーマンスが低下し、困っている」
「LaravelでのN+1問題の解消方法を知りたい」
こんな方には参考になるのではと思います。
それでは、早速見ていきましょう!
N+1問題とは
N+1問題とは、一つのデータベースクエリが発行され、その結果を元にN回の追加クエリが発行される状況のことを言います。
例えば、以下のようなコードです。
<?php
namespace App\Http\COntrollers;
class UsersController extends Controller
{
public function getUsers()
{
$users = User::all();
foreach ($users as $user) {
echo $user->team->name;
}
}
}
これは、以下のようなことを行なっています。
- 全てのユーザーを取得する(SQL1件)
- それぞれのユーザーのアカウント名を取得する(SQLN件)
N件となっているところは、ユーザーの数だけ増えます。ユーザーが100名いる場合は100回です。
これで、合計するとN+1件のSQL文を発行するため、N+1問題と呼ばれています。
ユーザー数が少ない場合は表面上の問題として出てくることはありませんが、ユーザー数が増えてくると、極端に処理が重くなります。
解消方法
withメソッドを使う
では、どうすればN+1問題を解消できるかというと、Laravelの場合はwithメソッドを使うのが一番簡単です。
withメソッドは、リレーションで繋がっているデータも一緒に取得してくれる大変便利なメソッドです。
<?php
namespace App\Http\Controllers;
class UsersController extends Controller
{
public function getUsers()
{
$users = User::with('team')->get();
foreach ($users as $user) {
echo $user->team->name;
}
}
}
これで、以下のように改善されました。
- 全てのユーザーを取得する(SQL1件)
- ユーザーに紐づくアカウントを一括で取得する(SQL1件)
以下の一文だけで、上記の2件分のSQLクエリを発行しています。
$users = User::with('team')->get();
以下のコードは、事前に取得しておいたアカウント名を繰り返し処理で出力しているだけです。SQL文は発行していません。
foreach ($users as $user) {
echo $user->team->name;
}
どれだけユーザー数が増えても、合計2件分のSQLクエリしか発行しません。
これで、N+1問題が解消されました。
joinを使う
他にも、場合によってはjoinを使う方法もあります。
<?php
namespace App\\Http\\COntrollers;
class UsersController extends Controller
{
public function getUsers()
{
$users = User::join('teams', 'users.team_id', '=', 'teams.id')
->select('users.*', 'teams.name as team_name')
->get();
foreach ($users as $user) {
echo $user->team_name;
}
}
}
こちらもwithメソッドと同様で、先にテーブルを結合してアカウントデータを取得してきています。
ただし、一般的にはwithメソッドを使った解消方法が推奨されています。
何らかの理由により、withメソッドが使えない場合などはjoinやleftJoinなどを検討してみると良いかと思います。
コメント