コードを読んでクエリ回数をカウントできるようになる

DBを操作するメソッドはクエリを実行する

 DBを操作するメソッドを呼び出すとクエリが実行される。

 よく使われるのは以下のメソッドだろう。

参照系
・find
・find_by
・where

作成・更新系
・create
・update
・save

削除
・destroy

 上記のwhere, update, saveを除いては、呼び出しの度に必ず1回クエリが実行される。
 updateはインスタンスに変更がある場合のみクエリを実行する。
 saveは新規レコードの場合、又は既存レコードで変更がある場合にクエリを実行する。(正確には update, saveはトランザクションのBEGIN, COMMITクエリを無条件に実行しているが、このクエリは便宜上無視している)

whereの挙動

 whereは初回は必ずクエリを実行するが、前回と同じ条件の場合、2回目以降は1回目の結果のキャッシュを返すのでクエリは実行されない。
 尚、ここでキャッシュされると言っているのはwhereが返すActiveRecord_Relationインスタンスに対して、前回と同じ条件で呼び出した場合のこと。

pry(main)> activerecord_relation = User.where(id:1);
# 初回はクエリを実行する
pry(main)> activerecord_relation.to_a;
  User Load (0.3ms) [Shard: master] SELECT `users`.* FROM `users` WHERE `users`.`id` = 1
# 同じ条件の場合、2回目以降はクエリを実行しない
pry(main)> activerecord_relation.to_a;
# 以下は毎回別のActiveRecord_Relationインスタンスが生成されるので毎回クエリが実行される
pry(main)> User.where(id:1).to_a;
  User Load (0.3ms) [Shard: master] SELECT `users`.* FROM `users` WHERE `users`.`id` = 1
pry(main)> User.where(id:1).to_a;
  User Load (0.3ms) [Shard: master] SELECT `users`.* FROM `users` WHERE `users`.`id` = 1

 上記の他に使用頻度が高いのはbelogs_to, has_manyなどで定義されたアソシエーションを呼び出す場合。
 アソシエーションは基本的*にwhereと同じように初回はクエリを実行し、同じ条件で呼び出した場合は2回目以降はキャッシュを返す。

* 通常、アソシエーションはカラムの値で関連レコードを検索するが、belogs_toなどのアソシエーションの定義の際にLambdaを渡すことで動的に検索条件を変化させることがでる。

まとめ

 基本的にはDB操作をするメソッド呼び出したら、初回の1回はクエリが実行されているのだな、と認識する。
 その上で、実装したコードを実行し、ログ(development.logなど)に流れるクエリの回数が概ね意図通りか確認する。
 この時、同じテーブルに対し複数回似たようなクエリが流れている場合は、それを省略し、パフォーマンスを改善する余地があるかもしれない。

コメント