こんにちは。
laravelのように、フレームワークを使っていると、データベースアクセスもORMを通して行えてかなり便利ですよね。
でも、普通にphpを書いている気分でSQLを発行出来るため、SQLの発行回数が多くなり、サイトの速度がかなり遅くなってしまう恐れもあります。
この記事では、リレーションを持ったテーブルをまとめて読み出すときに気をつけるべきSQLのクエリの回数と、laravelのORMツールであるEloquent で、withメソッド を使ってSQLの回数を減らして高速化を行なう方法を紹介します。
laravelでwithを使ってSQLの読み込み回数を減らす
Eloquentのメソッドと発行されるSQLの例
まず、実際に複数のテーブルからなるデータを読み出してみて、どの様にSQLが発行されているかを、telescopeというlaravelのデバッグツールを使って見てみましょう。
例:複数のTaskを持ったproject
データ構成:プロジェクトとタスク
下図は、複数のタスクを持つことが出来るプロジェクト データを表示してます。
それぞれのプロジェクトがもつタスクをチェックボックス付きで表示しています。例えば、プロジェクト「プロジェクトA」は、3つのタスクを持っています。
データベースの構成
データベースの構成としては、プロジェクトテーブルと、タスクテーブルがあり、タスクテーブルのカラムproject_id でタスクが所属するプロジェクトを識別しています。
データベースのprojectsテーブル
データベースのtasksテーブル
project_id で属するプロジェクトを指定。
プログラム構成とSQLの回数
プログラム構成
上記のようなデータベース構成のとき、下図のようにプロジェクトモデルで、hasManyメソッドを作っておくと、Projectクラスのインスタンスから tasks を呼び出すことで、タスクのデータにアクセスすることが出来ます。
たとえば、下図のように、$project->tasks とすると、$projectが指すプロジェクトが持っているタスクのコレクションにアクセスできます。先程のプロジェクト一覧のページを表示させるために、下図の赤枠のように、$project->tasks と指定すれば、そのプロジェクトが持つタスクのコレクションにアクセスすることが出来ます。下図のリストは、ちょうど、プロジェクト一覧ページで、プロジェクトとプロジェクトが持つタスクの一覧を表示しているところです。
SQLの回数
特に何も考えずにプログラミングして、先程のプロジェクト一覧にアクセスすると、SQLアクセスを6回していました。
クエリ:projects と tasks で別々に呼び出し
そのうち、taskテーブルへのアクセスがプロジェクトの個数と同じ4回となってます。
SQLアクセスはオーバヘッドがでかいし、データを扱うのに特化しているデータベース内部の処理のほうが、PHPの様に実行時に機械語に変換されるスクリプトの処理よりも高速のため、こうやって何度もSQLのクエリを発行すると、時間がかかる処理になってしまいます。
クエリ回数を減らす:withメソッドを使ってEagerロード(=積極的な読み出し)
これを解決するためには、project一覧をデータベースから読み出すときにwithメソッドを使って予めタスクデータも読み出してしまいます。これをEagerロードといいます。積極的な読み出しという意味です。
一覧のためにprojectの一覧を読み出すコードは下図のようになっていました。
ログインユーザのid が所有者となっているプロジェクトをwhereメソッドで取得している単純な方法ですね。
ここに、withを使ってhasManyで指定したtasksを強制的に読み出す指示をしてみます。
whereメソッドとgetメソッドの間に with(‘tasks’)を入れた形になってます。
簡単ですよね。この指示により、このプロジェクト一覧ページを読み出すときのクエリは以下のようになります。一つだけのクエリですべてのプロジェクトに属するタスクを読み出していますよね。
SQLの結合を使って複数のテーブルのデータをとってきて方法とは違いますが、withメソッドを使えば、projectがいくつ増えようと、1つのクエリで属するtaskをすべてとってくることが出来きます。
まとめ
ORMを使うとわすれがちなクエリの最適化ですが、withメソッドを使ってEagerロードすることによって、簡単に行うことが出来ます。
ただ、不要なものまでEagerローディングしてしまうと、逆に処理時間が長くなったり、メモリを消費してしまいます。withによるEagerロードは、あくまでも、後で読み込むデータを先にまとめて読み込むのに使うようにしてください。
withを使ったEagerローディングに関しては、laravelのマニュアルページに詳しく載っているので、withメソッドに関してもっと色々知りたくなったら参照してみてください。
僕も最近納品が終わったウェブサイトではlaravelを使っていたのですが、EagerローディングにはwithPivotを使ったり、withに第2引数で先読みするアイテムに制約条件を付けたリもしました。
そんな応用的な内容も別途記事にしていきたいと思います。メルマガでも、本記事のようにプログラミングの仕事をしていて使えそうな考え方やテクニックなどを紹介したり、記事更新をお知らせしたりしています。
よかったら、下のフォームからメルマガに登録してみてください。
メルマガ登録フォーム
コメント