Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

Rails의 러시아 인형 캐싱

오늘 우리는 Rails의 내장 프래그먼트 캐싱 이상으로 캐싱을 향상시키기 위한 전술로 러시아어 인형 캐싱에 대해 알아보겠습니다.

프래그먼트 캐싱

Rails의 내장 프래그먼트 캐싱을 사용할 때 렌더링된 뷰의 일부는 뷰 프래그먼트로 저장되고 다시 요청되면 재사용됩니다. 이러한 캐시된 조각은 부실이 될 때까지 재사용됩니다. , 즉 조각을 만든 후 표시하는 데이터가 변경되었기 때문에 오래된 것입니다. 더 자세한 내용을 보려면 이 이전 게시물에서 프래그먼트 캐싱에 대해 좀 더 자세히 설명합니다.

<블록 인용>

👋 단순한 캐싱보다 더 광범위한 성능에 대해 읽고 싶다면 Ruby 성능 모니터링 체크리스트를 확인하세요.

이는 특히 복잡한 보기 또는 렌더링된 부분이 많은 보기의 경우 속도를 크게 향상시키지만 러시아 인형 캐싱이라는 접근 방식을 두 배로 줄여 속도를 더 향상시킬 수 있습니다. .

이 캐싱 접근 방식을 사용할 때 전략의 이름을 따서 명명된 "마트료시카" 인형처럼 뷰 조각이 서로 내부에 배치됩니다. 캐시된 조각을 더 작은 조각으로 나누면 중첩된 조각 중 하나만 변경될 때 외부 캐시를 더 빠르게 렌더링할 수 있습니다.

러시아 인형 캐싱의 예

예를 들어 제품을 판매하는 상점을 예로 들어 보겠습니다. 각 제품에는 예를 들어 한 항목의 여러 색상을 판매할 수 있는 여러 변형이 있을 수 있습니다. 색인에는 판매 가능한 각 제품과 모든 변형이 표시됩니다.

제품 색인에서 각 제품을 캐시에 부분적으로 래핑했습니다. 차단하다. 제품을 사용 중입니다. 캐시된 조각을 무효화하는 데 사용되는 캐시 키를 빌드하기 위한 개체입니다. 이는 객체의 id, updated_at 날짜, 템플릿 트리의 요약으로 구성되어 있으므로 객체가 변경되거나 템플릿의 내용이 변경되면 자동으로 오래된 것으로 간주됩니다.

# app/views/products/index.html.erb
<h1>Products</h1>
 
<% @products.each do |product| %>
  <% cache product do %>
    <%= render product %>
  <% end %>
<% end %>

:명확성을 위해 전체 블록을 작성하고 있지만 <%=render partial:'products/product', collection:@products, cached:true %><를 사용하여 캐시 블록의 각 제품을 렌더링할 수 있습니다. /코드> 대신.

제품 부분에서 각 제품 변형에 대한 행을 렌더링합니다.

# app/views/products/_product.html.erb
<article>
  <h1><%= product.title %></h1>
 
  <ul>
    <% product.variants.each do |variant| %>
      <%= render variant %>
    <% end %>
  </ul>
</article>

캐시 무효화

Rails의 프래그먼트 캐싱에 있는 캐시 키를 사용하면 캐시 무효화가 더 쉬워지지만 캐시 유효성 검사(컴퓨터 과학에서 유명한 두 가지 어려운 점 중 하나)에 대해 걱정할 필요는 없습니다.

이 예에서는 제품의 변형 목록이 포함된 제품 부분을 캐시합니다. 캐시 키에는 변형에 대한 정보가 포함되어 있지 않으므로 제품 자체도 변경되지 않는 한 새로 추가된 변형은 표시되지 않습니다.

이 문제를 해결하는 방법은 제품이 변종 중 하나가 변경되면 변경됩니다. 이를 위해 제품의 updated_at를 업데이트합니다. 그럴 때마다 속성. 이것은 매우 일반적이므로 belongs_to에 대한 인수가 있습니다. (및 ActiveModel의 다른 관계 메서드), :touch , 상위 개체의 updated_at를 자동으로 업데이트합니다.

class Variant < ApplicationRecord
  belongs_to :product, touch: true
end

중첩 조각

변형이 변경될 때 제품 조각을 업데이트했으므로 이제 변형도 캐시해야 합니다. 이전과 마찬가지로 캐시 각 주변을 차단하십시오.

<article>
  <h1><%= product.title %></h1>
 
  <ul>
    <% product.variants.each do |variant| %>
      <% cache(variant) do %>
        <%= render variant %>
      <% end %>
    <% end %>
  </ul>
</article>

:명확성을 위해 전체 블록을 작성하고 있지만 <%=render partial:'variants/variant', collection:product.variants, cached:true %>를 사용하여 캐시 블록의 각 변형을 렌더링할 수 있습니다. 대신.

콜드 캐시에서(rake tmp:cache:clear를 실행하여 캐시를 지울 수 있습니다. ), 첫 번째 요청은 각 제품을 부분적으로 렌더링합니다.

지금 페이지를 요청할 때(개발 중인 rails dev:cache를 실행하여 캐싱을 켜는 것을 잊지 마세요. ), 각 제품 부분은 부분으로 캐시되고 두 번째 요청은 캐시된 조각을 반환합니다.

Started GET "/products" for 127.0.0.1 at 2018-03-30 14:51:38 +0200
Processing by ProductsController#index as HTML
  Rendering products/index.html.erb within layouts/application
  Product Load (0.2ms)  SELECT "products".* FROM "products"
  Variant Load (0.9ms)  SELECT "variants".* FROM "variants" WHERE "variants"."product_id" IN (27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51)
  Rendered variants/_variant.html.erb (0.5ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.0ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered products/_product.html.erb (44.8ms) [cache miss]
  ...
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered products/_product.html.erb (46.2ms) [cache miss]
  Rendered products/index.html.erb within layouts/application (1378.6ms)
Completed 200 OK in 1414ms (Views: 1410.5ms | ActiveRecord: 1.1ms)


Started GET "/products" for 127.0.0.1 at 2018-03-30 14:51:41 +0200
Processing by ProductsController#index as HTML
  Rendering products/index.html.erb within layouts/application
  Product Load (0.3ms)  SELECT "products".* FROM "products"
  Variant Load (12.7ms)  SELECT "variants".* FROM "variants" WHERE "variants"."product_id" IN (27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51)
  Rendered products/index.html.erb within layouts/application (48.1ms)
Completed 200 OK in 76ms (Views: 59.0ms | ActiveRecord: 13.0ms)

Voila:러시아 인형 마술

러시아 인형 캐싱의 마법은 변형 중 하나를 변경할 때 볼 수 있습니다. 변형 중 하나가 변경된 후 색인을 다시 요청하면 updated_at 속성이 변경되었습니다.

제품 부분에는 각 제품의 변형이 포함됩니다. 방금 변경한 변형의 캐시된 조각이 오래되었으므로 다시 생성해야 하지만 다른 변형은 변경되지 않았으므로 캐시된 조각이 재사용됩니다. 로그에서 변형과 제품 부분이 모두 한 번 렌더링되는 것을 볼 수 있습니다.

Started GET "/products" for 127.0.0.1 at 2018-03-30 14:52:04 +0200
Processing by ProductsController#index as HTML
  Rendering products/index.html.erb within layouts/application
  Product Load (0.3ms)  SELECT "products".* FROM "products"
  Variant Load (1.2ms)  SELECT "variants".* FROM "variants" WHERE "variants"."product_id" IN (27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51)
  Rendered variants/_variant.html.erb (0.5ms)
  Rendered products/_product.html.erb (13.3ms) [cache miss]
  Rendered products/index.html.erb within layouts/application (45.9ms)
Completed 200 OK in 78ms (Views: 73.5ms | ActiveRecord: 1.5ms)

최종 결과

이와 같이 캐시 조각을 중첩하면 캐시가 완전히 비어 있지 않는 한 뷰가 완전히 렌더링되지 않습니다. 데이터가 변경되더라도 대부분의 렌더링된 페이지는 캐시에서 바로 제공됩니다.

이 정보가 앱 성능에 대한 새로운 통찰력을 얻는 데 도움이 되었기를 바랍니다. 이것이 우리가 여기 있는 이유입니다. 캐싱에 대한 이 기사가 마음에 들었고 더 많은 정보가 필요하다면 ActiveRecord의 Counter Cache에 대한 게시물, Rails의 Cache Stores에 대한 게시물, 컬렉션 캐싱에 대한 게시물 및 프래그먼트에 대한 게시물이 추가로 제공됩니다. 게시물의 앞부분에서 언급한 Rails의 캐싱