地方でリモートワーク

プログラミング、先物、fx,仮想通貨なんでもやります

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を学ぶだけでなく、データベース周りも学習しないとですね。