Laravelで不要なクエリ発行を抑制する方法

アイキャッチ画像

DBのアクセス時に不要なリレーションロードを防ぐ仕組み、存在チェックの効率化、安全な一括処理、および予期せぬN+1の検知といった対策が必要になります。

ここでは具体的にどのようなメソッドを実装していくべきか紹介します。

N+1問題の検知

例えば次のような予約一覧を取得する処理があったとします。

予約一覧側で次のように会員情報を表示した場合

ページネーションのループごとにmembersテーブルを検索し会員名を検索するクエリが発行されてしまいます。これがN+1問題で、一覧を取得するクエリ+ループ分(この場合は合計11回)クエリが発行されます。

これに対応するため、with()を利用してeager loadingを指定する必要があるのですが、見逃してしまっても動作が遅くなるだけで見た目上は不具合があるとは分かりません。

そのため一覧やページネーションを取得する処理の中で次のメソッドを実行するように改修します。

※app()->isLocalなどと併用することを推奨します。

preventLazyLoadingを指定することで、N+1問題が発生した場合に例外が発生するため検知が容易になります。

存在チェックの効率化

レコードの存在の有無だけチェックする場合、不要なカラムを読み込む必要はありません。そのために次のような専用メソッドを作成します。

消費メモリの抑制

get()メソッドなどを使用して大量のレコードを一度に取得するとメモリ不足になり処理がタイムアウトしたり500エラーになったりします。

lazyByIdを使用すると遅延評価でループ処理ごとにデータを取得するため消費メモリを抑制できます。

不要カラム読み込みの抑制

一覧画面の表示時のように必要なカラムのみ取得できるように対応すると処理が軽量になります。

以下の例では必要なカラムを配列で渡すパターン(select_columns)、および複数テーブルをJOINした際にカラムを指定するためのパターン(raw_select_columns)に対応しています。

まとめ

4パターンの例をあげましたが、これ以外にもパフォーマンスチューニング出来ることは色々あります。

ただ「動けば良い」では良いシステムとはいえません。

  • なるべく早く
  • なるべく軽く
  • なるべく少ないクエリを実行
  • 必要最低限のカラム取得

などを意識したシステムを開発していきましょう。