4月からサーバーサイドエンジニアとして、弊社プロダクトTUNAGの開発を行っております。先日、弊社CTOの松谷とペアプロを行いました。1問題に複数回直面しました。1問題を解消する方法として、関連テーブルのデータを事前に読み込んでおき、キャシュしておくという方法を取ると思いますが、Railsではその方法としてpreload、eager_load、includesメソッドが用意されています。それぞれのメソッドの挙動の違いについて、都度調べ理解するようにしていましたが、ペアプロを通して理解が浅いと感じた為、今回はpreload、eager_load、includesの挙動の違いや使い所についてまとめていきます。6.0.3.4使用する関連付け下記のモデル間の関連付けを前提として各メソッドの挙動の違いを説明していきます。:deparmentsend各メソッドの挙動preload指定した関連テーブル毎に別クエリを作成、関連テーブルのデータ配列を取得し、キャッシュします。引数に多対多の関連先を渡した場合は、中間テーブルを介して取得しキャッシュします。preloadした関連先で絞り込みを行った場合は、例外を投げます。1eager_loadLEFT_OUTER_JOINで指定したデータを結合して関連テーブルのデータ配列を取得し、キャッシュします。引数として渡した関連先の要素で絞り込みを行うことが出来ます。1includes後述しますが、デフォルトではpreloadと同じ挙動、関連先のテーブルの要素で絞り込みを行った場合などはeager_loadと同じ挙動をします。selfendこの時点ではクエリを発行せず、それぞれincludes_values、eager_load_values、preload_valuesに値を格納しているだけです。その後、exec_queriesメソッド内でeager_loading?メソッドにより判定を行い、preloadとeager_loadを使い分けています。references_eager_loaded_tables?)endeager_loading?メソッドは下記の場合にtrueとなります。関連先のテーブルで絞り込みを行っているincludesメソッドの場合は2つ目の条件に当てはまる場合がeager_loading?がtrueとなります。詳細は割愛しますが、eager_loading?がtrueの場合はfind_with_associationsメソッド内で結合処理がされていきます。他テーブルを結合処理しているか、関連先のテーブルで絞り込みを行っている場合はeager_loadと同じ挙動になるメソッドの実装の通り、複数のアソシエーションを引数で渡したとしても、どちらか片方のみpreload、もう片方はeager_loadを行うという挙動にはなりません。どちらのアソシエーションもpreloadされるか、eager_loadされるという挙動になります。preload、eager_loadの使い分け上述の内容を踏まえると、includesはクエリを制御しにくいため、基本的に優先してpreload、eager_loadを使用する方が良いと考えられます。その上でpreload、eager_loadの使い所を考えてみます。複数の関連先の事前読み込みを行う場合eager_loadでは常に結合処理を行うため、データ量が大きいと考えられる条件下では、重いクエリを実行することになってしまう。クエリを分割して事前読み込みを行った方がレスポンスが早くなると考えられます。※主テーブルのレコード数が多い場合は、IN句が大きくなってしまうため、DB側での設定値を確認する必要があるようです。関連先が上記のように○対1で関連付けられている場合、外部結合しても取得するレコード数に変わりは無く、1クエリでデータが取得出来るためpreloadより効率が良いと考えられます。最後にpreload、eager_load、includesの挙動や使いどころについてまとめてみました。パフォーマンスの問題はレコード数が増加するにつれて顕在化してくるため、未然に防止する為にも、日頃からよりパフォーマンスを意識したコードを書くことが出来るように意識していきたいと思います。興味を持っていただいた方は、是非下記の募集ページを御覧ください。開発を体験しようこんにちは。株式会社スタメンでは、エンジニア採用を強化しています。ご興味のある方はぜひエンジニア採用情報をご覧ください。引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。