6116


Rails（フレームワーク問わず）での、一般的なSQL処理回数について質問です。

処理回数というのは、例えばユーザがルートページとする「ダッシュボード」というページがあるとします。  
そのページを処理するとき、ユーザ情報等をDBより取得します。

そのSQL発行回数を抑えるように設計するのは当然ですが、皆さんの考え方として、「しきい値となる回数」  
はどの程度でしょうか？（このactionでは5回もSQLを取得しているから多すぎる、設計を見直そう。など）

その点で具体的にお聞きしたいこともあります。  
以下のテーブルがあるとします

  * `users`（`User has_one :setting`)
  * `user_settings`（`User::Setting belongs_to :user`）

Deviseなどのgemを使えば、`current_user`ヘルパーでユーザ情報を取得できますが、  
`user_settings`情報も同時に取得したい場合、

`@user_setting = current_user.setting`とすると思います。

しかし同時にsettingの情報も取得したいので、

[code]

    @current_user ||= User.includes(:setting).references(:setting).find(user_id)
    
[/code]

という風に書けば、SQL発行は１回で済むと思います。

しかし、eager_load, preload, キャッシュなどの情報があり、ベストプラクティスとしてはどのような考え方なのかがわかりません。

例えば、`current_user.setting`で取得して、`current_user`と`@user_setting`を２回のSQLで取得するのと、referencesを使ってjoinして取得するのとでは、SQLコストは同一として問題ないとして、  
Rails内のメモリ管理（キャッシュ）の機構で影響がないのか？などです。

また、`current_user`ヘルパーが

[code]

    def current_user
      @current_user ||= User.find(id) #idは何かしらで取得
    end
    
[/code]

となっていた場合、  
知識がなく確信がないのですが、`||=`としておくと一度のアクセスで`current_user`は１度のSQL処理しかしないという記述がありました（[Railsチュートリアル](http://railstutorial.jp/chapters/sign-
in-sign-out?version=4.0#sec-sessions_controller)）。

しかし、`includes`と`references`を利用すると`current_user`ヘルパーで`current_user`を呼び出すたびにSQLが呼び出されているログが確認できました（ログが`User
Load`ではなく`SQL (4.3ms)`となっているのはそういうことですよね？）。

`includes`, `references`を使わずに、個別に取得したほうがよいのでしょうか？


CC BY-SA 3.0
2015-02-07T11:35:57.273
6102
2015-02-07T15:58:35.887
2015-02-07T12:23:34.853
85
512
question
ruby-on-rails
Railsを使ったWEBサービスでのSQL取得回数


質問の内容をざっくりまとめると、DBアクセス回数を減らすことにどこまでこだわるべきか？ということだと理解しましたが、合っているのでしょうか？

僕に言わせるなら、この質問に対しては「ケースバイケースです」というあまり身のない回答になってしまいます。

もちろんDBアクセス数は減らせるなら減らすに越したことはないと思いますが、どこまで神経質にこだわるべきかはアプリケーションの性質に寄ります。

常に大量のアクセスをさばく必要があり、レスポンスが何よりも大事、というアプリケーションであれば徹底的にDBアクセスを減らすべし、という方針も出てくると思います。（そもそも、他にもチューニングポイントがたくさんありそうです）

逆にそういうアプリケーションでなければ、そこまで神経質にDBアクセスを減らす必要はないと思います。  
簡単な工夫で回避できるならそれで良いと思いますが、アクセス数を減らすことにこだわるあまり、コードがトリッキーになってしまうぐらいなら、僕はコードのシンプルさを取ります。  
そしてDBアクセス数がボトルネックになってきたタイミングで、なんらかのパフォーマンスチューニングを検討します。

というわけで、「ケースバイケースです」という答えになってしまうのはそういう理由からなのでした。

もし、kntmrkmさんが「まだRailsアプリを学習し始めたばかり」という段階なのであれば、コードのシンプルさを優先する、ということで良いと思います。  
テーブルの一覧表示など、普通に実行するとN+1問題が発生することが明らかでなおかつ、コード的にも簡単に `join` や `includes`
を組み合わせられる時だけ、そういった処理を入れるのが良いと思います。

ちなみに、N+1問題に関してはbulletというgemを導入すると、自動的に警告を上げてくれるので結構便利かもしれません。

<http://ruby-rails.hatenadiary.com/entry/20141109/1415522242>

以上ご参考までに。

### 追伸

RailsやSQLに限らず、パフォーマンス改善全般の考え方や戦略についてはCODE COMPLETEの下巻(第25章)が役に立ちます。  
これを読むと、「こういうケースはどうすべきか」という指針が自分の中で立てられるようになるかもしれません。

[CODE COMPLETE 第2版
下](http://www.amazon.co.jp/gp/product/4891004568?ie=UTF8&camp=1207&creative=8411&creativeASIN=4891004568&linkCode=shr&tag=junic05-22&refRID=0T6PBF6NV27NBF17AE7G)


CC BY-SA 3.0
2015-02-07T12:40:38.757
6116
2015-02-07T12:54:10.077
2015-02-07T12:54:10.077
85
85
6102
answer


パフォーマンス問題のベストプラクティスは「プロファイルしてから考えましょう」です。なんとなくDBアクセスは減らしたくなりますが、Viewの処理に100倍時間がかかっていたら気にするのは無駄でしょう。

DBアクセスに絞って検討するにしても、単純な回数では無く、時間と回数のトータルで判断すべきです。1回にまとめられるはずのクエリが10回あったら改善の余地があるかもしれませんが、それよりも複雑な結合が必要とかインデックス使ってないとかデータが大きいとかそういう重たいクエリをどうにかするほうがたいていは効率的です。


CC BY-SA 3.0
2015-02-07T15:58:35.887
6125
2015-02-07T15:58:35.887
5793
6102
answer