Rails: パーシャルと`collection:`でN+1クエリを回避してビューを高速化(翻訳)

概要 原著者の許諾を得て翻訳・公開いたします。 英語記事: Partial rendering performance in Rails 原文公開日: 2017/11/17 著者: Alessandro Rodi Rails: パーシャルとcollection:でN+1クエリを回避してビューを高速化(翻訳) Railsでビューのレンダリング(特にパーシャル)を正しく行うことの重要性に気づいてない人をよく見かけます。本記事では、さまざまなアプローチのパフォーマンスの相対数値を比較します。このトピックは、多くのブログ記事で見落とされがちです。 N+1クエリの回避 最初の重要なトピックは「N+1クエリ」です。N+1クエリをのさばらせると速度低下が不可避になり、その他のパフォーマンス最適化も効かなくなってしまうことがあるため、ぜひとも回避しましょう! 非常にシンプルな例から見てみましょう。 <% @users.each do |*user*| %> <div class=”post”> <%= *user*.post.title %> </div> <% end %> usersのリストを反復して、各userをpostとしています。簡単ですね。 それでは、ユーザー1000人を以下のコードでassignしてテストしてみましょう。 assign(:users, *User*.all) 結果は以下のとおりです。 Warming up — — — — — — — — — — — — — — — — — — — with N+1 1.000 i/100ms Calculating — — — — — — — — — — — — — — — — — — – with N+1 0.291 (± 0.0%) i/s — 2.000 in 7.158333s 1秒あたりでレンダリング可能なビュー数は0.291です。これはかなり残念な数値なので、ビューで発生しているN+1クエリを最初に解決しましょう。現在のビューでは、イテレーションのたびにDBでSELECTを実行してuserのpostを取り出しています。 N+1を今後も解決するためにBullet gemを導入します。私はBulletが大好きです❤️。次の3つの理由からGoldiloaderよりもBulletが好みです。 Bulletのコード自動更新は変更してよいかどうかをプロンプトで確認してくれる。 Bulletはproduction環境では実行できないようになっている。 BulletはN+1クエリを隠蔽せず、検出と理解を支援してくれる。 GemfileにBulletを追加してtest環境向けに設定します。これによって、N+1クエリで例外がraiseされ、解決しないとテストを完了できないようになります。 # app/config/environments/test.rb config.after_initialize do Bullet.enable = true Bullet.bullet_logger = true Bullet.raise = true end テストを再実行すると、必要な情報をBulletが表示してテストを失敗させます。 Bullet::Notification::UnoptimizedQueryError: USE eager loading detected User => [:post] Add to your finder: :includes => [:post] そしてassignを以下のように変更します。 assign(:users, User.includes(:post)) 修正後の結果は7倍ほど高速になりました。 Warming up ————————————– with N+1 1.000 i/100ms without N+1 1.000 i/100ms Calculating ————————————- with N+1 1.539 (± 0.0%) i/s – 8.000 in 5.305367s without N+1 10.479 (± 9.5%) i/s – 52.000 in 5.057764s Comparison: without N+1: 10.5 i/s with N+1: 1.5 i/s – 6.81x slower パーシャルのレンダリング それでは本記事の本題であるパーシャルの利用に進みましょう。個別のpostをパーシャルに切り出してコードをリファクタリングすることにします。これはよい方法ですが、次のような駄目リファクタリングがあることも知っておきましょう。 … Continue reading Rails: パーシャルと`collection:`でN+1クエリを回避してビューを高速化(翻訳)