【Rails】初心者でもできるパフォーマンス改善方法

はじめに

こんにちは、株式会社TOKOSの石津です!
今回はRailsで開発されたアプリケーションのパフォーマンス改善方法について説明していきます。

実用されるアプリケーションの開発においてパフォーマンス改善は必須です。
しかしRuby on Railsを学び始めた方にとっては、パフォーマンスなんて気にしたことがない方がほとんどだと思います。

今回はパフォーマンス改善方法といっても、初心者の自分にはどうせ理解できないだろうという方のために簡単にできる方法を説明していくので安心してください!

対象読者

  • railsで開発されたアプリケーションのパフォーマンスを改善したい人
  • railsを勉強し始めた人

なぜパフォーマンスが悪くなるのか

パフォーマンスの改善方法を説明する前にパフォーマンスが悪くなる原因を説明していきます
Ruby on Railsで開発されたアプリケーションでパフォーマンスに一番影響しているといっても過言ではないのがデータベースへのアクセス回数です。

データベースへのアクセスは以下の場合に行われています。

User.all
User.find(params[:id])
User.where(nickname: "tanaka")

データベースへのアクセスは1コードにつき1回行われるわけではなく、各メソッドやモデルの親子関係によっても変わってきます。
パフォーマンスが悪くなる時はデータベースへのアクセスが複数回ある時が多いです。

今回は普段使っている関数の中で、データベースへのアクセス回数を増やす原因となっている箇所を挙げていきます。

1. N+1問題の解決

N+1問題は、データベースへのアクセス回数を著しく増加させる原因の一つです。
これは1つの親レコード(User)に対して複数の子レコード(Product)を持つ場合に発生し、親レコード1件ごとに子レコードを取得するためのクエリが発行されるため、データベースへのアクセスが増加します。

例えば10個の親レコードがそれぞれ10個の子レコードを持つ場合、N+1問題が発生している状態では100回(1回の親レコード取得 + 10回×10の子レコード取得)のデータベースアクセスが発生します。

それを解決する方法が以下の方法になります!

#この記述だとデータベースへのアクセスが増える
User.all

User.includes(:products)
#または
User.preload(:products)
#または
User.eager_load(:products)

includes・preload・eager_loadのいずれかを使うことによってデータベースへのアクセス方法を減らすことができます!

これらの関数を使うことによって、親要素のレコードを取得するためにデータベースへアクセスする際に子要素のレコードも同時に取得してくれるので子要素分のアクセスを減らすことができます。
どれを使えばいいかに関しては、取得したいデータやデータ量に応じて使い分ける必要がありますが初心者の方はひとまずincludesで問題ないです。

2. 必要なカラムのみ取得

Userテーブルのnameカラムのデータのみ使いたい場合以下のように記述することによりパフォーマンスを改善することができます。

User.pluck(:name)

pluckメソッドは特定のカラムのみを配列として取得してくれます。
特定のカラムのみを取得することで、データベースからのデータ転送量が減少し、結果としてクエリの実行速度が向上します。
これは大量のデータを扱う場合や、ネットワーク経由でデータベースにアクセスする場合に顕著にパフォーマンスの改善を確認することができます。

またUserという完全なモデルオブジェクトのインスタンス化を避けることができます。
これにより、全てのカラムデータを含むオブジェクトを作成するよりもメモリ使用量を大幅に削減することができます。

3. present?とexists?の使い分け

対象の存在判定をする場合にpresentやexistを使っている方は多いと思います。
present?メソッドとexist?メソッドも使用方法を適切に使うことによりパフォーマンスを改善することができます。

exists?

exists?メソッドはSQLクエリを生成し、データベースに対して直接実行されます。
必要なデータのみを検索し、フルテーブルスキャンや全レコードの読み込みを避けることができます。

データベースへのレコードの存在判定には、exists?メソッドを使うことが適しています。

present?

present?メソッドはオブジェクトが実際にデータを含んでいるかを確認するのに適しています。
データベースから取得したデータに対して存在判定を行います。

データベースから取得したデータに対してはpresent?を使用する

exists?はSQLを発行してしまうので、データベースにアクセスすることになってしまいます。
データベースから取得したデータが存在する場合はpresent?を使うようにしましょう。

4. countを使用しない

要素の数を数える場合にcountメソッドを使用することが多いと思います。
このcountメソッドもクエリを発行しデータベースにアクセスし数を数えています。

数を数える場合にも、present?と同様に既にデータベースから取得したデータに対して数を数えるメソッドがあります。

それがlengthメソッドとsizeメソッドです。

lengthメソッドはpresent?と同様に既にデータベースから取得したデータに対して数を数えます。

sizeメソッドは既にデータベースから取得したデータがあった場合はその数を数え、なかった場合はデータベースにアクセスしデータを取得します。
sizeメソッドは非常に便利なので是非使ってみてください!

おわりに

今回の記事ではRailsで作られたアプリケーションのパフォーマンス改善方法を説明しました。
ポイントはいかにデータベースにアクセスする回数を減らせるかです!

この記事を参考に高パフォーマンスのアプリケーションを開発してくれる人いたら嬉しいです。