Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Railsコレクションのキャッシング

以前、AppSignalAcademyのRailsでのフラグメントキャッシングについて見てきました。これにより、ビューの小さな部分がキャッシュされるため、ビューのパフォーマンスが大幅に向上します。パーシャルをキャッシュする場合、ビューの他の場所でわずかなコストでそれらを再利用できるという追加の利点があります。

これは小さなコレクションではうまく機能しますが、大きなコレクションではすぐに問題が発生します。この記事では、Railsコレクションのキャッシュがどのように機能するか、そしてそれを使用して大規模なコレクションのレンダリングを高速化する方法を見ていきます。

👋そして、この記事が気に入ったら、Ruby(on Rails)のパフォーマンスについて書いたものがもっとたくさんあります。Rubyのパフォーマンス監視チェックリストをチェックしてください。

コレクションのレンダリング

ブログのインデックスページの最後の100件の投稿を読み込む小さなコントローラーから始めましょう。

class PostsController < ApplicationController
  def index
    @posts = Post.all.order(:created_at => :desc).limit(100)
  end
end

これらの投稿をビューに表示するには、@postsをループします インスタンス変数。

<!-- app/views/posts/index.html.erb -->
<h1>Posts</h1>
 
<div class="posts">
  <% @posts.each do |post| %>
    <div class="post">
      <h2><%= post.title %></h2>
      <small><%= post.author %></small>
 
      <div class="body">
        <%= post.body %>
      </div>
    </div>
  <% end %>
</div>

このページをリクエストすると、データベースから投稿がフェッチされ、ビューがレンダリングされているのがわかります。ビューレイヤーで32ミリ秒しか費やされていないため、このページはかなり高速です。

Started GET "/posts"
Processing by PostsController#index as HTML
  Rendering posts/index.html.erb within layouts/application
  Post Load (1.5ms)  SELECT  "posts".* FROM "posts" ORDER BY "posts"."created_at" DESC LIMIT ?  [["LIMIT", 100]]
  ↳ app/views/posts/index.html.erb:4
  Rendered posts/index.html.erb within layouts/application (19.4ms)
Completed 200 OK in 37ms (Views: 32.4ms | ActiveRecord: 2.7ms)

パーシャルを使用したコレクションのレンダリング

次に、postを使用します 別のビューの要素なので、投稿HTMLをパーシャルに移動します。

<!-- app/views/posts/index.html.erb -->
<h1>Posts</h1>
 
<div class="posts">
  <% @posts.each do |post| %>
    <%= render post %>
  <% end %>
</div>
 
<!-- app/views/posts/_post.html.erb -->
<div class="post">
  <h2><%= post.title %></h2>
  <small><%= post.author %></small>
 
  <div class="body">
    <%= post.body %>
  </div>
</div>
Started GET "/posts"
Processing by PostsController#index as HTML
  Rendering posts/index.html.erb within layouts/application
  Post Load (1.2ms)  SELECT  "posts".* FROM "posts" ORDER BY "posts"."created_at" DESC LIMIT ?  [["LIMIT", 100]]
  ↳ app/views/posts/index.html.erb:4
...
  Rendered posts/_post.html.erb (0.1ms)
  Rendered posts/_post.html.erb (0.1ms)
  Rendered posts/index.html.erb within layouts/application (205.4ms)
Completed 200 OK in 217ms (Views: 213.8ms | ActiveRecord: 1.7ms)

ビューレイヤーに213ミリ秒が費やされたため、レンダリング時間が大幅に増加したことがわかります。これは、投稿ごとに新しいファイル(部分)をロード、コンパイル、およびレンダリングする必要があるためです。フラグメントキャッシングを使用してレンダリング時間を改善する方法を簡単に見てみましょう。

フラグメントキャッシング

フラグメントキャッシングの記事で説明されているように、cacheを使用します render周辺のビューのヘルパー 電話。このようにして、すべての投稿のパーシャルのレンダリングをキャッシュします。

<!-- app/views/posts/index.html.erb -->
<h1>Posts</h1>
 
<div class="posts">
  <% @posts.each do |post| %>
    <%= cache post do %>
      <%= render post %>
    <% end %>
  <% end %>
</div>
Started GET "/posts"
Processing by PostsController#index as HTML
  Rendering posts/index_with_partial_caching.html.erb within layouts/application
  Post Load (1.4ms)  SELECT  "posts".* FROM "posts" ORDER BY "posts"."created_at" DESC LIMIT ?  [["LIMIT", 100]]
  ↳ app/views/posts/index.html.erb:4
...
Read fragment views/posts/index.1ms)
  Rendered posts/_post.html.erb (0.1ms)
Write fragment views/posts/index.1ms)
Read fragment views/posts/index.5ms)
  Rendered posts/_post.html.erb (0.1ms)
Write fragment views/posts/index.1ms)
  Rendered posts/index.html.erb within layouts/application (274.5ms)
Completed 200 OK in 286ms (Views: 281.4ms | ActiveRecord: 2.4ms)

最初のリクエストは、最初にすべての部分をレンダリングしてキャッシュストアに保存する必要があるため、それほど速くはなりません。

Started GET "/posts"
Processing by PostsController#index as HTML
  Rendering posts/index.html.erb within layouts/application
  Post Load (2.2ms)  SELECT  "posts".* FROM "posts" ORDER BY "posts"."created_at" DESC LIMIT ?  [["LIMIT", 100]]
  ↳ app/views/posts/index.html.erb:4
...
Read fragment views/posts/index.1ms)
Read fragment views/posts/index.1ms)
  Rendered posts/index.html.erb within layouts/application (63.8ms)
Completed 200 OK in 78ms (Views: 75.5ms | ActiveRecord: 2.2ms)

後続のリクエストでは、ビューで費やされる時間が286ミリ秒から78ミリ秒に大幅に短縮されていることがわかります。それでも、元のコードで得られたものよりもはるかに低速です。ほぼ2倍の速度です。

注:ログに「フラグメントの読み取り/書き込み」行が表示されない場合は、開発環境でフラグメントキャッシュロギングを有効にしてください。これはfalseに設定されています。 Rails 5.1以降のデフォルト:

# config/environments/development.rb
config.action_controller.enable_fragment_cache_logging = true

コレクションキャッシング

Rails 5では、コレクションのキャッシュを高速化するために多くの作業が行われました。これらの改善を活用するには、ビューコードを変更する必要があります。 cacheを呼び出す代わりに ヘルパー自身、Railsにコレクション全体をレンダリングし、同時にキャッシュするように依頼できます。

<!-- app/views/posts/index.html.erb -->
<h1>Posts</h1>
 
<div class="posts">
  <%= render partial: :post, collection: @posts, cached: true %>
</div>

render @collection, cached: trueに注意してください このキャッシング速度の向上には速記は機能しません。

Started GET "/posts"
Processing by PostsController#index as HTML
  Rendering posts/index.html.erb within layouts/application
  Post Load (1.4ms)  SELECT  "posts".* FROM "posts" ORDER BY "posts"."created_at" DESC LIMIT ?  [["LIMIT", 100]]
  ↳ app/views/posts/index.html.erb:4
  Rendered collection of posts/_post.html.erb [0 / 100 cache hits] (28.2ms)
  Rendered posts/index.html.erb within layouts/application (46.6ms)
Completed 200 OK in 64ms (Views: 59.9ms | ActiveRecord: 2.0ms)

最初のリクエストでは、ビューレイヤーに費やされる時間が大幅に改善されていることがすでにわかります。これは、Railsが事前に準備し、一部が各投稿に個別に使用されるのではなく、コレクション全体に使用されるようになったためです。

Started GET "/posts"
Processing by PostsController#index as HTML
  Rendering posts/index.html.erb within layouts/application
  Post Load (1.3ms)  SELECT  "posts".* FROM "posts" ORDER BY "posts"."created_at" DESC LIMIT ?  [["LIMIT", 100]]
  ↳ app/views/posts/index.html.erb:4
  Rendered collection of posts/_post.html.erb [100 / 100 cache hits] (19.2ms)
  Rendered posts/index.html.erb within layouts/application (26.5ms)
Completed 200 OK in 37ms (Views: 35.7ms | ActiveRecord: 1.3ms)

その後のリクエストでは、64ミリ秒から約35ミリ秒にさらに改善が見られます。ここでは、コレクションのRails最適化により、コレクション全体の速度が大幅に向上しています。 Railsは、パーシャルごとにキャッシュの可用性をチェックする代わりに、コレクションのすべてのキャッシュキーを同時にチェックするため、キャッシュストアのクエリにかかる時間を節約できます。

このキャッシングヘルパーの追加の利点は、コレクションの要約されたロギングです。最初のリクエストでは、キャッシュキーが見つかりませんでした[0 / 100 cache hits] 、しかし2番目のリクエストでは、すべて[100 / 100 cache hits]が見つかりました 。

データベース内のいくつかのオブジェクトを更新した後、古くなったキーの数を確認することもできます。

Rendered collection of posts/_post.html.erb [88 / 100 cache hits] (13.4ms)

この最適化されたコレクションのレンダリングとキャッシングにより、速度が大幅に向上します。より大きなコレクションをレンダリングすると、さらに大きな違いが生じます。コレクションのビューをカスタマイズする必要がない限り、この最適化された戦略がRailsアプリを選択する方法です。 AppSignalでは、この方法で、何千ものレコードをレンダリングしていた管理ビューの1つを大幅に高速化することができました。

Railsでのコレクションのキャッシュについて質問がありますか? @AppSignalまでお気軽にお知らせください。記事に関するコメントがある場合、または私たちがカバーしたいトピックがある場合は、私たちに連絡してください。


  1. RailsをAWSLambdaにデプロイする

    サーバーレスコンピューティングは、サーバーの管理とプロビジョニングの作業をクラウドプロバイダーに任せるのに役立ち、ほとんどのテクノロジーチームにとって急速に重要になっています。 AWS Lambdaは、多くのテクノロジーチームで使用されているサーバーレステクノロジーの一種です。 AWS Lambdaは、NodeJS、Java、Python、Rubyなどのコアプログラミング言語のほとんどをサポートしています。コアプログラミング言語はサポートされていますが、これらの言語で構築されたフレームワークの一部である機能に依存してサーバーレス関数を実行したい場合があります。この投稿では、AWSLambdaで

  2. Rails5でのAngularの使用

    あなたは前にその話を聞いたことがあります。分散型で完全に機能するバックエンドAPIと、通常のツールセットで作成されたフロントエンドで実行されているアプリケーションがすでにあります。 次に、Angularに移動します。または、AngularをRailsプロジェクトと統合する方法を探しているだけかもしれません。これは、この方法を好むためです。私たちはあなたを責めません。 このようなアプローチを使用すると、両方の世界を活用して、たとえばRailsとAngularのどちらの機能を使用してフォーマットするかを決定できます。 構築するもの 心配する必要はありません。このチュートリアルは、この目的のた