【Rails】eager_loadとpreload使い分けの基準

仕事でパフォーマンス改善のタスクをしており、N+1問題を解消しようとしていました。その際にeager_loadpreloadのどちらを使うべきか迷ったため、備忘録として判断基準をまとめます。

バージョン

  • Ruby 3.0.3
  • Rails 6.1.7.7

記事の信頼性

  • ぼくは独学で未経験から従業員300名以上の自社開発企業へ転職しました。
  • 実務ではVue.jsとRailsを毎日書いています。
  • 初心者や駆け出しエンジニアがつまづくポイントも身をもってよく理解しています。
目次

問題

パフォーマンス改善のタスクで、N+1問題が発生している箇所を見つけました。

修正のためには以下の箇所で関連モデル(Category、User、Company、Summary、MetaInfo、Review)の事前読み込みが必要です。

# where句の後に事前読み込みの処理を入れたい
Task.where(id: task_ids, status: :suspended).each do |task|
  # 中身の処理は省略
end

この6モデルはすべてTaskモデルに対してbelongs_tohas_oneの関係にあります。

この場面でeager_loadpreloadのどちらを使うべきか、判断がつきませんでした。

# 以下のどちらが最適か判断がつかない
Task.where(id: task_ids, status: :suspended)
  .eager_load(:user, :company, :summary, category: [:metainfo, :review]).each do |task|
  # 中身の処理は省略
end

Task.where(id: task_ids, status: :suspended)
  .preload(:user, :company, :summary, category: [:metainfo, :review]).each do |task|
  # 中身の処理は省略
end

現在の状況をまとめておきます。

  • テーブル数: 6つ
  • 各テーブルのカラム数: 20以上
  • 各テーブルのレコード数: 数万以上

解決方法

結論から言うと、今回はpreloadを採用することになりました。

以下に、eager_loadpreloadそれぞれの特徴と、今回preloadを選択した理由をまとめます。

eager_load を使う場合

eager_loadは、SQLのJOIN句を使って関連テーブルのデータを1つのクエリで取得します。

今回のように結合するテーブルが6つあり、各テーブルのカラム数が20以上ある場合、JOIN句が複雑になってしまいます。

そして複雑なJOIN句は、データベースのオプティマイザにとってクエリの最適化が難しくなります。

さらにレコード数も多いため、1つのクエリで大量のデータを取得すると実行時間が長くなる可能性があると考えました。

preload を使う場合

preloadは、メインのクエリとは別に、関連テーブルのデータを個別のクエリで取得します。

今回のケースだと、メインのクエリとは別に、6回のクエリを発行するイメージです。

シンプルなクエリを複数発行することになるため、テーブル結合のコストを回避でき、クエリの複雑さに起因する問題は発生しにくいです。

もちろん、クエリの数が増える分だけパフォーマンスは悪化しますが、今回のようなデータ量の場合はpreloadの方が適していると判断しました。

preload を選択した理由

1. テーブル結合のコストを回避できる
2. クエリの複雑さに起因する問題が発生しにくい
3. データ量が多い場合、シンプルなクエリを複数発行する方が効率的

ローカル環境に大量にデータを用意して両者のパフォーマンスを10回ずつ計測してみたんですが、平均値・中央値・ブレ幅ともにpreloadの方が優れていました。

10回の中にはeager_loadのパフォーマンスが優れていたときもあったので、明確な差とまでは言い切れないんですが、安定感も含めてpreloadを採用しました。

おわりに

今回のようにデータ量が多い場合は、preloadを使ってクエリを分割することで、パフォーマンスの改善が期待できると思いました。

preloadを使うとバカでかいクエリを発行してしまうリスクを回避できるため、ある程度重い処理のときはpreloadを使う方がベターなのかな?と感じました。

参考になれば幸いです。

Railsエンジニアにおすすめの記事

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

未経験でSESから従業員300名以上の自社開発企業に転職しました。業務や個人開発で直面した問題や、転職・学習の経験を発信していきます。

コメント

コメントする

目次