RailsのN+1回カウントするクエリを改善するactiverecord-precount


Ruby on Railsをモデルでidごとのcountを取るような場合に発生しがちなN+1 countクエリを高速化するRubyGems『activerecord-precount』を紹介します。

🗻 概要

次のようなN+1カウントのクエリが発生する場合:

Tweet.all.each do |tweet|
p tweet.favorites.count
end
# SELECT `tweets`.* FROM `tweets`
# SELECT COUNT(*) FROM `favorites` WHERE `favorites`.`tweet_id` = 1
# SELECT COUNT(*) FROM `favorites` WHERE `favorites`.`tweet_id` = 2
# SELECT COUNT(*) FROM `favorites` WHERE `favorites`.`tweet_id` = 3

precountメソッドを使うことで、2つのSQLでid単位のcountを取れます。

Tweet.all.precount(:favorites).each do |tweet|
p tweet.favorites_count
end
# SELECT `tweets`.* FROM `tweets`
# SELECT COUNT(`favorites`.`tweet_id`), `favorites`.`tweet_id` FROM `favorites` WHERE `favorites`.`tweet_id` IN (1, 2, 3, 4, 5) GROUP BY `favorites`.`tweet_id`

Joinも合わせて行いたい場合はeager_countメソッドを使うことで、1つのSQLでcountをとれます。

Tweet.all.eager_count(:favorites).each do |tweet|
p tweet.favorites_count
end
# SELECT `tweets`.`id` AS t0_r0, `tweets`.`tweet_id` AS t0_r1, `tweets`.`user_id` AS t0_r2, `tweets`.`created_at` AS t0_r3, `tweets`.`updated_at` AS t0_r4, COUNT(`favorites`.`id`) AS t1_r0 FROM `tweets` LEFT OUTER JOIN `favorites` ON `favorites`.`tweet_id` = `tweets`.`id` GROUP BY tweets.id

🎳 インストール手順

Gemfileに次の内容を追加してbundle installを実行します。

# N+1 count query killer for ActiveRecord
gem 'activerecord-precount'

🐹 参考リンク

🖥 VULTRおすすめ

VULTR」はVPSサーバのサービスです。日本にリージョンがあり、最安は512MBで2.5ドル/月($0.004/時間)で借りることができます。4GBメモリでも月20ドルです。 最近はVULTRのヘビーユーザーになので、「ここ」から会員登録してもらえるとサービス開発が捗ります!

📚 おすすめの書籍