【Rails】count→sizeに変更しパフォーマンス改善した話

仕事でパフォーマンス改善のタスクを遂行していました。countメソッドの使用を避けることでパフォーマンスを向上できることがわかったため、備忘録としてまとめます。

バージョン

  • Ruby 3.0.3
  • Rails 6.1.7.7

記事の信頼性

  • ぼくは独学で未経験から従業員300名以上の自社開発企業へ転職しました。
  • 実務ではVue.jsとRailsを毎日書いています。
  • 初心者や駆け出しエンジニアがつまづくポイントも身をもってよく理解しています。
目次

問題

パフォーマンス改善のタスクで、先輩エンジニアから「countが使われているからsizeにした方がいい」とアドバイスをいただきました。

修正対象のコードは次の2行です。

def index
  # 他の記述は省略
  @withdrawals_count = withdrawals.count
  @withdrawals_total_amount = withdrawals.sum(&:amount)
  # 他の記述は省略
end

withdrawalsはActiveRecordから取得した、出金記録に関するオブジェクトです。

下記のように修正することになりました。

def index
  # 他の記述は省略
  @withdrawals_total_amount = withdrawals.sum(&:amount)
  @withdrawals_count = withdrawals.size
  # 他の記述は省略
end

1行目と2行目を入れ替えた上で、countメソッドをsizeメソッドに変更しただけです。

ひとまず修正したものの、なぜこれがパフォーマンス改善につながるのか理解できませんでした、、。

解決方法

結論として、countメソッドとsizeメソッドの内部挙動の違いによるものでした。

  • count:使うたびに毎回SQLを発行する
  • size:すでにメモリに読み込まれている場合はSQLを発行しない

もう一度、修正前のコードを見てみましょう。

def index
  # 他の記述は省略
  @withdrawals_count = withdrawals.count                # 1回目のSQL発行
  @withdrawals_total_amount = withdrawals.sum(&:amount) # 2回目のSQL発行
  # 他の記述は省略
end

この書き方だと、countsumで合計2回SQLが発行されることになります。

次に修正後のコードを再確認します。

def index
  # 他の記述は省略
  @withdrawals_total_amount = withdrawals.sum(&:amount) # 1回目のSQL発行
  @withdrawals_count = withdrawals.size                 # 2回目のSQLは発行されない!
  # 他の記述は省略
end

withdrawals.sum)を実行した時点で、withdrawalsがメモリに読み込まれるため、withdrawals.sizeの実行時にSQLは発行されません。

つまり、SQLの発行回数を減らせるため、パフォーマンス改善につながります!

おわりに

ActiveRecordの裏で発行されるSQLの回数まで意識したことがなかったため、とても勉強になりました。

これまでパフォーマンス改善のタスクではN+1の解消くらいしかできていませんでしたが、これを機により深くRailsのパフォーマンス改善と向き合っていきたいです。

Railsエンジニアにおすすめの記事

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

未経験でSESから従業員300名以上の自社開発企業に転職しました。業務や個人開発で直面した問題や、転職・学習の経験を発信していきます。

コメント

コメントする

目次