Caching er afgørende for at forbedre ydeevnen og skalerbarheden af webapplikationer – og caching i Ruby on Rails er ingen undtagelse. Ved at gemme og genbruge resultaterne af dyre beregninger eller databaseforespørgsler reducerer caching betydeligt den tid og de ressourcer, der kræves for at betjene brugerforespørgsler.

Her gennemgår vi, hvordan man implementerer forskellige typer af caching i Rails, såsom fragment-caching og Russian doll-caching. Vi viser dig også, hvordan du håndterer cache-afhængigheder og vælger cache-stores og skitserer bedste praksis for at bruge caching effektivt i en Rails-applikation.

Denne artikel forudsætter, at du er fortrolig med Ruby on Rails, bruger Rails version 6 eller højere og føler dig tryg ved at bruge Rails views. Kodeeksemplerne demonstrerer, hvordan man bruger caching i nye eller eksisterende view-skabeloner.

Typer af Ruby on Rails-cache

Der findes flere typer af caching i Ruby on Rails-applikationer, afhængigt af niveauet og granulariteten af det indhold, der skal caches. De primære typer, der bruges i moderne Rails-apps, er:

  • Fragment-caching: Cacher dele af en webside, der ikke ændres ofte, såsom headers, footers, sidebars eller statisk indhold. Fragmentcaching reducerer antallet af delsider eller komponenter, der gengives på hver anmodning.
  • Russian doll caching: Cacher indlejrede fragmenter af en webside, der er afhængige af hinanden, som f.eks. samlinger og associationer. Russian doll caching forhindrer unødvendige databaseforespørgsler og gør det nemt at genbruge uændrede cachelagrede fragmenter.

Yderligere to typer af caching var tidligere en del af Ruby on Rails, men er nu tilgængelige som separate gems:

  • Page caching: Cacher hele websider som statiske filer på serveren og omgår hele sidens renderingslivscyklus
  • Action caching: Cacher output fra hele controller-handlinger. Det svarer til page caching, men giver dig mulighed for at anvende filtre som autentificering.

Page- og action-caching bruges sjældent og anbefales ikke længere til de fleste brugsscenarier i moderne Rails-apps.

Fragment-caching i Ruby on Rails

Fragment-caching lader dig cache dele af en side, der ændres sjældent. For eksempel kan en side, der viser en liste over produkter med tilhørende priser og bedømmelser, cache detaljer, som sandsynligvis ikke vil ændre sig.

I mellemtiden kan det lade Rails genskabe dynamiske dele af siden – som kommentarer eller anmeldelser – ved hver sideindlæsning. Fragment-caching er mindre nyttigt, når en visnings underliggende data ændres hyppigt på grund af de omkostninger, der er forbundet med hyppige opdateringer af cachen.

Som den enkleste form for caching i Rails, bør fragment-caching være dit første valg, når du tilføjer caching til din app for at forbedre ydeevnen.

For at bruge fragment-caching i Rails skal du bruge hjælpemetoden cache i dine views. Skriv for eksempel følgende kode for at cache en produktdel i din visning:

<% @products.each do |product| %>
  <% cache product do %>
    <%= render partial: "product", locals: { product: product } %>
  <% end %>
<% end %>

cache -hjælperen genererer en cache-nøgle baseret på hvert elements klassenavn, id, og updated_at -tidsstempel (for eksempel products/1-20230501000000). Næste gang en bruger anmoder om det samme produkt, vil cache -hjælperen hente det cachelagrede fragment fra cachelageret og vise det uden at læse produktet fra databasen.

Du kan også tilpasse cache-nøglen ved at sende indstillinger til cache -hjælperen. Hvis du f.eks. vil inkludere et versionsnummer eller et tidsstempel i din cache-nøgle, skal du skrive noget i denne stil:

<% @products.each do |product| %>
  <% cache [product, "v1"] do %>
    <%= render partial: "product", locals: { product: product } %>
  <% end %>
<% end %>

Alternativt kan du angive en udløbstid:

<% @products.each do |product| %>
  <% cache product, expires_in: 1.hour do %>
    <%= render partial: "product", locals: { product: product } %>
  <% end %>
<% end %>

Det første eksempel vil tilføje v1 til cache-nøglen (for eksempel products/1-v1). Dette er nyttigt til at ugyldiggøre cachen, når du ændrer den delvise skabelon eller layout. Det andet eksempel angiver en udløbstid for cache-posten (1 time), hvilket hjælper med at udløbe forældede data.

Russian doll-cache i Ruby on Rails

Russian doll caching er en effektiv caching-strategi i Ruby on Rails, som optimerer din applikations ydeevne ved at indlejre caches i hinanden. Den bruger Rails fragment-caching og cache-afhængigheder til at minimere overflødigt arbejde og forbedre indlæsningstiderne.

I en typisk Rails-applikation renderer du ofte en samling af elementer, hver med flere underordnede komponenter. Når du opdaterer et enkelt element, skal du undgå at genskabe hele samlingen eller alle upåvirkede elementer. Brug Russian Doll-caching, når du har at gøre med hierarkiske eller indlejrede datastrukturer, især når de indlejrede komponenter har deres egne tilknyttede data, der kan ændre sig uafhængigt af hinanden.

Ulempen ved Russian Doll-caching er, at det øger kompleksiteten. Du skal forstå relationerne mellem de indlejrede niveauer af elementer, du cacher, for at sikre, at du cacher de rigtige elementer. I nogle tilfælde bliver du nødt til at tilføje associationer til dine Active Record-modeller, så Rails kan udlede relationerne mellem de cachede dataelementer.

Som med almindelig fragment-caching bruger Russian doll-caching hjælpemetoden cache. Hvis du f.eks. vil cache en kategori med dens underkategorier og produkter i din visning, skal du skrive noget i denne stil:

<% @categories.each do |category| %>
  <% cache category do %>
    <h2><%= category.name %></h2>
    <% category.subcategories.each do |subcategory| %>
    <% cache subcategory do %>
    <h3><%= subcategory.name %></h3>
    <% subcategory.products.each do |product| %>
    <% cache product do %>
        <%= render partial: "product", locals: { product: product } %>
        <% end %>
    <% end %>
    <% end %>
    <% end %>
  <% end %>
<% end %>

cache -hjælperen gemmer hvert indlejret niveau separat i cache-lageret. Næste gang der anmodes om den samme kategori, vil den hente det cachelagrede fragment fra cachelageret og vise det uden at rendere det igen.

Men hvis en underkategori eller et produkts detaljer ændres – som navn eller beskrivelse – ugyldiggøres det cachelagrede fragment, som derefter gengives med opdaterede data. Russian doll caching sikrer, at du ikke behøver at ugyldiggøre en hel kategori, hvis en enkelt underkategori eller et produkt ændres.

Håndtering af cache-afhængighed i Ruby on Rails

Cache-afhængigheder er relationer mellem cachelagrede data og deres underliggende kilder, og det kan være vanskeligt at styre dem. Hvis kildedataene ændres, bør alle tilknyttede cachede data udløbe.

Rails kan bruge tidsstempler til at styre de fleste cache-afhængigheder automatisk. Hver Active Record-model har created_at og updated_at attributter, der angiver, hvornår cachen oprettede eller sidst opdaterede posten. For at sikre, at Rails automatisk kan styre caching, skal du definere dine Active Record-modellers relationer som følger:

class Product < ApplicationRecord
  belongs_to :category
end
class Category < ApplicationRecord
  has_many :products
end

I dette eksempel:

  • Hvis du opdaterer en produktoptegnelse (for eksempel ved at ændre prisen), ændres dens updated_at tidsstempel automatisk.
  • Hvis du bruger dette tidsstempel som en del af din cache-nøgle (som products/1-20230504000000), invaliderer det også automatisk dit cachelagrede fragment.
  • For at ugyldiggøre din kategoris cachelagrede fragment, når du opdaterer en produktpost – måske fordi den viser nogle aggregerede data som gennemsnitspris – skal du bruge touch -metoden i din controller (@product.category.touch) eller tilføje en touch -indstilling i din modelassociation (belongs_to :category touch: true).

En anden mekanisme til at styre cache-afhængigheder er at bruge caching-metoder på lavt niveau – såsom fetch og write – direkte i dine modeller eller controllere. Disse metoder giver dig mulighed for at gemme vilkårlige data eller indhold i din cache-butik med brugerdefinerede nøgler og muligheder. For eksempel:

class Product < ApplicationRecord
  def self.average_price
    Rails.cache.fetch("products/average_price", expires_in: 1.hour) do
    average(:price)
    end
  end
end

Dette eksempel viser, hvordan man cacher beregnede data – såsom gennemsnitsprisen for alle produkter – i en time ved hjælp af fetch -metoden med en brugerdefineret nøgle (products/average_price) og en udløbsindstilling (expires_in: 1.hour).

Metoden fetch vil først forsøge at læse dataene fra cache-lageret. Hvis den ikke kan finde dataene, eller hvis dataene er udløbet, udfører den blokken og gemmer resultatet i cache-lageret.

Hvis du manuelt vil ugyldiggøre en cache-post før dens udløbstid, skal du bruge metoden write med optionen force:

Rails.cache.write("products/average_price", Product.average(:price), force: true))

Cache-stores og backends i Ruby on Rails

Rails giver dig mulighed for at vælge forskellige cache stores eller backends til at gemme dine cachelagrede data og indhold. Rails cache store er et abstraktionslag, der giver en fælles grænseflade til at interagere med forskellige lagersystemer. En cache-backend implementerer cache-store-grænsefladen for et specifikt lagersystem.

Rails understøtter flere typer cache-stores eller backends fra start, som er beskrevet nedenfor.

Hukommelseslager

Memory store bruger en in-memory hash som cache-lager. Det er hurtigt og enkelt, men har begrænset kapacitet og persistens. Denne cache store er velegnet til udviklings- og testmiljøer eller små, enkle applikationer.

Disklager

Disk store bruger filer på disken som cache-lager. Det er den langsomste caching-mulighed i Rails, men har stor kapacitet og vedholdenhed. Disk store er velegnet til applikationer, der skal cache store mængder data og ikke har brug for maksimal ydeevne.

Redis

Redis store bruger en Redis-instans til cache-lagring. Redis er et in-memory datalager, der understøtter flere datatyper. Selvom det er hurtigt og fleksibelt, kræver det en separat server og konfiguration. Det er velegnet til applikationer, der skal cache komplekse eller dynamiske data, der ændres ofte. Redis er et ideelt valg, når man kører Rails-apps i skyen, fordi nogle hostingudbydere, herunder Kinsta, tilbyder Redis som en persistent objektcache.

Memcached

Memcached-butikken bruger en Memcached-instans til cache-lagring. Memcached er en in-memory key-value store, der understøtter simple datatyper og funktioner. Det er hurtigt og skalerbart, men ligesom Redis kræver det en separat server og konfiguration. Dette lager er velegnet til applikationer, der har brug for at cache simple eller statiske data, som opdateres hyppigt.

Du kan konfigurere din cache-butik i dine Rails-miljøfiler (f.eks. config/environments/development.rb) ved hjælp af indstillingen config.cache_store. Her kan du se, hvordan du bruger hver af Rails’ indbyggede cachemetoder:

# Use memory store
config.cache_store = :memory_store
# Use disk store
config.cache_store = :file_store, "tmp/cache"
# Use Redis
config.cache_store = :redis_cache_store, { url: "redis://localhost:6379/0" }
# Use Memcached
config.cache_store = :mem_cache_store, "localhost"

Du bør kun have ét config.cache_store -kald pr. miljøfil. Hvis du har mere end ét, bruger cache-lageret kun det sidste.

Hver cache store har unikke fordele og ulemper afhængigt af din applikations behov og præferencer. Vælg den, der passer bedst til din brugssituation og dit erfaringsniveau.

Bedste praksis for Ruby on Rails-caching

Brug af caching i din Rails-applikation kan øge dens ydeevne og skalerbarhed betydeligt, især når du implementerer følgende best practices:

  • Cache selektivt: Cach kun data, der ofte tilgås, er dyre at generere eller opdateres sjældent. Undgå at overcache for at forhindre overdreven brug af hukommelse, risiko for uaktuelle data og forringelse af ydeevnen.
  • Udløb af cache-poster: Forebyg forældede data ved at udløbe ugyldige eller irrelevante poster. Brug tidsstempler, udløbsindstillinger eller manuel ugyldiggørelse.
  • Optimer cache-ydelsen: Vælg det cachelager, der passer til din applikations behov, og finjuster dets parametre – som størrelse, komprimering eller serialisering – for optimal ydeevne.
  • Overvåg og test cache-effekten: Evaluer cache-adfærd – som hit rate, miss rate og latency – og vurder deres respektive indvirkning på performance (responstid, throughput, ressourceforbrug). Brug værktøjer som New Relic, Rails logs, ActiveSupport notifikationer eller Rack mini profiler.

Opsummering

Ruby on Rails caching forbedrer applikationens ydeevne og skalerbarhed ved effektivt at gemme og genbruge data eller indhold, der ofte tilgås. Med en dybere forståelse af caching-teknikker er du bedre rustet til at levere hurtigere Rails-apps til dine brugere.

Når du implementerer din optimerede Rails-applikation, kan du henvende dig til Kinstas Applikation Hosting-platform. Kom i gang gratis med en Hobby Tier-konto, og udforsk platformen med dette Ruby on Rails quick-start-eksempel.

Steve Bonisteel Kinsta

Steve Bonisteel is a Technical Editor at Kinsta who began his writing career as a print journalist, chasing ambulances and fire trucks. He has been covering Internet-related technology since the late 1990s.