Railsでincludesをしなかったらクエリ数が異常に多くなった

f:id:ihatov08:20170313091034j:plain

Railsで開発をしていると、必然的にActive Recordを使うことになると思います。

Active Recordを使うとSQLを書くことなくDB問い合わせができるので、便利な反面、
SQLや、キャッシュ、メモリを理解していないとパフォーマンスが悪くなってしまうことが
しばしばあります。

自分もまだ全然理解できていません。

今回1万件のデータをCSV生成する際に、関連モデルが多数あるモデルからallで引っ張ってきた場合と、
関連テーブルとincludesした場合のパフォーマンスを検証してみました。
ユーザーに、カテゴリテーブル、所属会社テーブルなど6つのテーブルが紐づいています。

Benchmarkを使う

RubyのライブラリであるBenchmarkを使います。
rails consoleでrequireして速度測定しました。

$ rails c
[1] pry(main)> require 'benchmark'
=> true

includesした場合

今回は以下のようなscopeを作成しました。

  scope :all_association_includes, -> do
includes(:business_outline, :domicile_company, :job_category, :prefecture, :introducer_last_work_company)
end
  • クエリ数 = 6
result = Benchmark.realtime do
User.to_csv('include_ver.csv')
end
User Load (327.4ms)  SELECT "users".* FROM "users" WHERE ("users"."_at" IS NOT NULL)
BusinessOutline Load (0.3ms)  SELECT "business_outlines".* FROM "business_outlines" WHERE "business_outlines"."id" = 1
DomicileCompany Load (1.2ms)  SELECT "domicile_companies".* FROM "domicile_companies" WHERE "domicile_companies"."id" = 1
JobCategory Load (1.1ms)  SELECT "job_categories".* FROM "job_categories" WHERE "job_categories"."id" = 1
Prefecture Load (1.1ms)  SELECT "prefectures".* FROM "prefectures" WHERE "prefectures"."id" = 1
DomicileCompany Load (0.1ms)  SELECT "domicile_companies".* FROM "domicile_companies" WHERE "domicile_companies"."id" = 1
puts result
=> 33.34830200000215

includeしない場合

モデルをallで全件取得。

User.all
result = Benchmark.realtime do
User.to_csv('not_include_ver.csv')
end
クエリ数 = 尋常じゃない数
puts result
=> 151.05989799999952

結果

速度に5倍近く差が出ました。原因はクエリ数です。includesしない場合はUser一件取得ごとに関連テーブルのデータを取得するクエリが走りるのが原因です。

RailsやRubyを学ぶだけでなく、データベース周りも学習しないとですね。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です