LoginSignup
4
4

More than 3 years have passed since last update.

RailsのN+1対策について

Last updated at Posted at 2019-03-19

ActiveRecordのメソッドについて

RailsでN+1対策をする時に使用するメソッド3つeager_load, preload, includesについて使用するケースをメモします。

eager_load

  • 引数に指定したassociationをLEFT OUTER JOINで取得してキャッシュします。
  • preloadと違い一度のクエリで値を取得します。
$ User.eager_load(:posts)
#=> SELECT `users`.`id` AS t0_r0, `users`.`name` AS t0_r1, `users`.`created_at` AS t0_r2, `users`.`updated_at` AS t0_r3, `posts`.`id` AS t1_r0, `posts`.`user_id` AS t1_r1, `posts`.`created_at` AS t1_r2, `posts`.`updated_at` AS t1_r3 FROM `users` LEFT OUTER JOIN `posts` ON `posts`.`user_id` = `users`.`id`
  • whereメソッド等をチェインできます。
  • 対象テーブルのデータサイズが大きくjoinのコストが大きい場合にパフォーマンスが悪くなる場合があります。

preload

  • 引数に指定したassociationをLEFT OUTER JOINで取得してキャッシュします。
  • eager_loadと違い複数のクエリで値を取得します。
$ User.preload(:posts)
#=> SELECT `users`.* FROM `users`
#=> SELECT `posts`.* FROM `posts` WHERE `posts`.`user_id` IN (1, 2, 3, ...)
  • whereメソッド等をチェインできません。
  • 対象テーブルのデータサイズが大きくjoinのコストが大きい場合に有効です。

includes

Activerecordが最終的に生成するクエリに合わせてeager_loadpreloadの挙動を行う。

個人的に、includesを使う事は以下の理由からオススメしません。
後で自分が見た時や他の人が見た時にeager_loadpreloadのどちらを使用したかったのか意図がわからなくなる為です。意図が読み解けたとしても、そこに掛ける時間がもったいないので、コードを書いた時に意図が分かるようにしておいた方が良いと思います。
コードリーディング時よりも、チューニングする時に意図がわからず困る可能性が高いです。

eager_loadpreloadのどちらを使うか

SQLは単純にクエリ発行数を減らせば良い場合とそうでない場合があり、テーブルサイズ、レコード数などの条件によって判断する必要があります。

例えば、データサイズが小さい、レコード数が少ないテーブルを処理する場合はクエリ発行数の少ないeager_loadが有効な場合が多いと思います。

逆に、データサイズが大きい、レコード数が多いテーブルをeager_loadで処理しようとした場合に、データがDBのワークメモリに収まりきれずディスク使用が発生し遅くなるという事もありえます。その場合はpreloadの方が有効です。

一方でMySQLを使っている場合は、preloadで発行されるIN句の中身が多い場合にテーブルフルスキャンが実行される事がある為、スロークエリの温床になる事があります。

参考

https://qiita.com/k0kubun/items/80c5a5494f53bb88dc58
https://qiita.com/ostk0069/items/23beb870adf785506be2
https://moneyforward.com/engineers_blog/2019/04/02/activerecord-includes-preload-eagerload/

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4