Engineering from Scratch

エンジニア目指してます

2022/06/13

eager_load と preload の使い所

includes

eager_loadpreload を呼び分ける。どちらかが呼ばれるかコントロールしにくいので,基本使わない。実装を読んでみようと思ったが,読み切るには重そうだったので,週末読む。

eager_load

belongs_tohas_oneに使用する。

1:N となるの関係となる,user:blogについて考える。user.eager_load(:blogs).limit(10を実行した時,limitは,userに対して行われ,以下のような結果となる。

irb(main):014:0> User.count
  User Count (0.2ms)  SELECT COUNT(*) FROM "users"
=> 12
irb(main):015:0> Blog.count
  Blog Count (0.2ms)  SELECT COUNT(*) FROM "blogs"
=> 1200
irb(main):011:0> User.eager_load(:blogs).limit(10)
  SQL (0.3ms)  SELECT DISTINCT "users"."id" FROM "users" LEFT OUTER JOIN "blogs" ON "blogs"."user_id" = "users"."id" LIMIT ?  [["LIMIT", 10]]
  SQL (6.8ms)  SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, "users"."department_id" AS t0_r2, "users"."company_id" AS t0_r3, "users"."created_at" AS t0_r4, "users"."updated_at" AS t0_r5, "blogs"."id" AS t1_r0, "blogs"."title" AS t1_r1, "blogs"."user_id" AS t1_r2, "blogs"."created_at" AS t1_r3, "blogs"."updated_at" AS t1_r4 FROM "users" LEFT OUTER JOIN "blogs" ON "blogs"."user_id" = "users"."id" WHERE "users"."id" IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)  [["id", 1], ["id", 2], ["id", 3], ["id", 4], ["id", 5], ["id", 6], ["id", 7], ["id", 8], ["id", 9], ["id", 10]]

この時,userを一意なものにしてから取得するので,distinctが走り,これがスロークエリの原因となる模様。詳しい実装は割愛。

preload

has_manyに使用する。

注意点としては,IN 句が膨大にならないように注意する。

参考

https://moneyforward.com/engineers_blog/2019/04/02/activerecord-includes-preload-eagerload/