ActiveRecordはどのタイミングで実際にクエリを実行しているのか。SQLキャッシュについて。

目次

はじめに

ActiveRecordのメソッドを使ってDBアクセスすることができますが
実際にDBアクセスするタイミングは、実は即時でなかったりします(遅延評価)。

この辺りの理解を深めて、よりパフォーマンスを意識したコードが書けるように整理していきたいと思います。

また、最後に、同じリクエスト、同じ呼び出しであればRailsのモデルにキャッシュされるため
実際にDBアクセスは行われないので、この辺りにも触れたいと思います。

ActiveRecordの遅延評価

ActiveRecordクエリを作成する場合、多くの場合、コードはデータベースの即時呼び出しを実行しません。
これにより、毎回データベースにアクセスすることなく、.whereなどで複数の句を連鎖させることができます(メソッドチェーン)。

これにはいくつかの例外があり、例えばfindfind_byの句を連鎖させることはできません。つまり、このタイミングでクエリが実行されることになります。

ActiveRecord Relations

以下のようなUser-Postsの1対多の関係があるとします。

これにより、データベースクエリを実行して関連するレコードを見つけるための便利なuser.postsメソッドが提供されます。

Railsアプリケーション内で、以下のサンプルコードを実行したとします。
@user.postsではクエリを実行せずに、@posts.each...、この時点でActiveRecordはデータベースクエリを実行してデータを取得します。

実際にログでも、@user.postsではクエリが実行されず、@posts.eachの部分で、クエリが実行されていました。

(ついでに)
以下のコードを実行した場合、同じリクエスト、同じ呼び出しはRailsのモデルにキャッシュされるため、2回目の@posts.eachではクエリは実行されません。キャッシュからデータを取り出します。

ActiveRecordに関連するレコードを再度取得したいとき

ActiveRecordのreloadをメソッドチェーンすると、キャッシュされたものを全て無視して、データベースから最新バージョンを取得するように指示させることができます。

SQL CACHE

ActiveRecordは、パフォーマンスを高速化するために実行したクエリの内部キャッシュを保持してくれます。
ただし、このキャッシュは特定のアクションに関連付けられているので注意が必要です。アクションの開始時に作成され、アクションの終了時に破棄されます。
(1つのコントローラーアクション内で同じクエリを2回実行した場合など)

Railsアプリケーション内で、以下のようなサンプルコードを実行したとします。

実際にログを確認すると、ひとつめの呼び出しでは実際にSQLが実行されているのが分かります。
ふたつめの呼び出しでは、SQLの実行結果がすでにRailsのモデルにキャッシュされているため、実際にDBアクセスは行われず、モデルのキャッシュからデータを取得していることになります。

ちなみにirbなどのコンソールの場合では、SQLのキャッシュは行われないため
挙動の確認は、実際にアプリケーションを動かしてみるのがいいかと思います。

まとめ

ActiveRecordは便利ですが、扱いには気をつけたいです!