Railsでのフラグメントキャッシング
Railsアプリケーションがリクエストを受け入れると、コントローラーは通常、モデルにリクエストされたデータを要求します。次に、モデルはそれをデータベースからフェッチし、コントローラーに返します。コントローラは最終的に、人間が読める形式でデータを表すビューをレンダリングします。
状況によっては、ビューのレンダリングはコストのかかる操作です。特に、ストアで利用可能なすべての商品のリストを表示する場合など、ビューに大量のデータを表示する必要がある場合はそうです。そのような場合、特にデータがあまり頻繁に変更されない場合は、返されたビューの一部をキャッシュすることで処理を高速化できます。
👋そして、この記事が気に入ったら、Ruby(on Rails)のパフォーマンスについて書いたものがもっとたくさんあります。Rubyのパフォーマンス監視チェックリストをチェックしてください。
ローカルでのキャッシュのテスト
開発中はデフォルトでキャッシュがオフになっているため、アプリから常に最新の応答を受け取ることができます。ローカルでキャッシュをテストするには、開発構成でキャッシュをオンにする必要があります。
Rails 5では、コマンドラインから一時的にキャッシュをオンにすることができます。これはメモリストアを使用します。つまり、キャッシュされたフラグメントはWebサーバーのRubyプロセスのメモリに保持されます。
$ rails dev:cache
Development mode is now being cached.
同じコマンドを実行して、キャッシュをオフに戻すことができます。
フラグメントキャッシング
ストア内のすべての商品を1つのページに表示するページがあるとします。そのために、製品を表示するインデックスビューがあります。
# app/views/products/index.html.erb
<table>
<thead>
<tr>
<th>Title</th>
<th>Description</th>
<th>Image url</th>
<th>Price</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @products.each do |product| %>
<%= render product %>
<% end %>
</tbody>
</table>
製品ごとに、_product.html.erb
部分的にレンダリングされ、製品の詳細を含むテーブル行の表示が処理されます。
# app/views/products/_product.html.erb
<tr>
<td><%= product.title %></td>
<td><%= product.description %></td>
<td><%= product.image_url %></td>
<td><%= product.price %></td>
<td><%= link_to 'Show', product %></td>
<td><%= link_to 'Edit', edit_product_path(product) %></td>
<td><%= link_to 'Destroy', product, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
データベースに25個の製品がある開発モードで、ローカルでページを要求するには、約300ミリ秒かかります。その時間のほとんどすべてがパーシャルのレンダリングに費やされています。
ほとんどの場合、アセットがバンドルされ、ロギングが少なく、Webサーバーが高速であるため、本番環境での応答が速くなります。開発中の数値は正確ではありませんが、リクエストのどの部分が最も遅いかが示されるため、スピードアップを試みることができます。
Started GET "/products" for ::1 at 2018-03-13 12:16:08 +0100
Processing by ProductsController#index as HTML
Rendering products/index.html.erb within layouts/application
Product Load (0.4ms) SELECT "products".* FROM "products"
Rendered products/_product.html.erb (1.4ms)
Rendered products/_product.html.erb (0.4ms)
Rendered products/_product.html.erb (0.4ms)
Rendered products/_product.html.erb (0.3ms)
Rendered products/_product.html.erb (0.5ms)
Rendered products/_product.html.erb (2.0ms)
Rendered products/_product.html.erb (0.9ms)
Rendered products/_product.html.erb (0.4ms)
Rendered products/_product.html.erb (0.5ms)
Rendered products/_product.html.erb (0.5ms)
Rendered products/_product.html.erb (0.6ms)
Rendered products/_product.html.erb (0.6ms)
Rendered products/_product.html.erb (0.6ms)
Rendered products/_product.html.erb (0.6ms)
Rendered products/_product.html.erb (0.7ms)
Rendered products/_product.html.erb (0.6ms)
Rendered products/_product.html.erb (0.7ms)
Rendered products/_product.html.erb (0.6ms)
Rendered products/_product.html.erb (0.5ms)
Rendered products/_product.html.erb (0.7ms)
Rendered products/_product.html.erb (0.6ms)
Rendered products/_product.html.erb (0.9ms)
Rendered products/_product.html.erb (0.6ms)
Rendered products/_product.html.erb (0.5ms)
Rendered products/_product.html.erb (0.6ms)
Rendered products/index.html.erb within layouts/application (257.5ms)
Completed 200 OK in 295ms(Views: 290.4ms | ActiveRecord: 0.4ms)
これらすべてのパーシャルをレンダリングする時間を節約するために、Railsの組み込みのフラグメントキャッシングを使用できます。 、レンダリングされたビューの一部をフラグメントとして保存します。以降のリクエストでは、事前に保存されたフラグメントが再度レンダリングされる代わりに使用されます。
フラグメントをキャッシュするには、cache
を使用してフラグメントをブロックにラップします ヘルパー。
<table>
# ...
<tbody>
<% @products.each do |product| %>
<% cache(product) do %>
<%= render product %>
<% end %>
<% end %>
</tbody>
</table>
これらの製品をキャッシュすることで応答が速くなるかどうかを確認するために、ページを2回リクエストします。ビュー内の各製品は事前にレンダリングされ、すでにキャッシュに保存されているため、2番目のリクエストははるかに高速に実行されるはずです。
Started GET "/products" for ::1 at 2018-03-13 12:17:29 +0100
Processing by ProductsController#index as HTML
Rendering products/index.html.erb within layouts/application
Product Load (0.4ms) SELECT "products".* FROM "products"
Rendered products/index.html.erb within layouts/application (21.2ms)
Completed 200 OK in 55ms (Views: 50.8ms | ActiveRecord: 0.4ms)
出来た! 2番目のリクエストは最初のリクエストより5倍以上速かった。パーシャルはキャッシュから直接ロードされるため、ログにはパーシャルのレンダリングは表示されないことに注意してください。
キャッシュの期限切れ
cache
を呼び出すとき 上記の例のヘルパーでは、product
を渡しました キャッシュ依存関係としてのオブジェクト 。これにより、キャッシュされたフラグメントのコンテンツが製品オブジェクトに依存していることがヘルパーに通知されます。
内部的には、製品オブジェクトには#cache_key
があります キャッシュされたフラグメントのキーを作成するために使用されるメソッド。フラグメント全体のキーは次のようになります:
views/products/42-20180302103130041320/75dda06d36880e8b0ae6cac0a44fb56d
キャッシュキーはいくつかの部分で構成されています:
- 「views/products」はキャッシュクラスです 。
-
42
製品のIDです -
20180302103130041320
製品のupdated_at日付です -
75dda06d36880e8b0ae6cac0a44fb56d
テンプレートツリーのダイジェストです
このキーは、製品が更新されるたび、またはテンプレート内の何かが変更されるたびに変更されます。その場合、キャッシュでミスが発生し、フラグメントが再度レンダリングされ、新しいフラグメントが保存されます。これにより、コンポーネントが変更された場合でも、フラグメントが新鮮なままになります。
フラグメントキャッシングを超えて
この例で説明したキャッシュフラグメントはわずかなスピードアップをもたらしますが、キャッシュには今日説明した以上のものがあります。ロシアの人形のキャッシュなどの戦略や、データベースクエリ結果のキャッシュなどの低レベルの方法を使用することで、より大きなスピードアップを実現できます。
もちろん、これらについては、AppSignalAcademyの後半のエピソードで説明します。何か具体的に知りたいことはありますか? @AppSignalまでお気軽にお知らせください。もちろん、この記事がどのように気に入ったか、または別のテーマがある場合はもっと知りたいと思います。
-
HTML段落
HTML段落は、タグを使用して段落要素を作成するために使用されます。これはブロックレベルの要素です。 構文 以下は構文です- <p>content</p> 例 HTML段落の例を見てみましょう: <!DOCTYPE html> <html> <style> body { color: #000; height: 100vh; background-color: #8BC6EC; &
-
HTMLレイアウト
HTMLレイアウトは、HTMLWebページ上のコンポーネントの配置を指定します。 Webページのさまざまなセクションを定義する多くのHTMLセマンティック要素があります。 HTMLレイアウトに使用されるセマンティックHTML要素は次のとおりです。 タグ 説明 ヘッダー セクションまたはドキュメントのヘッダーを指定します。 セクション ドキュメント内のセクションを表します。 nav ナビゲーションリンクのコンテナを指定します。 記事 独立した自己完結型の記事を指定します。 脇 メインコンテンツ以外のコンテンツ(サイドバーなど)のタグを指定します。 フッター セクション