Caching ist wichtig, um die Leistung und Skalierbarkeit von Webanwendungen zu verbessern – und Caching in Ruby on Rails ist da keine Ausnahme. Durch das Speichern und Wiederverwenden der Ergebnisse teurer Berechnungen oder Datenbankabfragen reduziert das Caching die Zeit und die Ressourcen, die für die Bearbeitung von Nutzeranfragen benötigt werden, erheblich.
Wir zeigen dir, wie du verschiedene Arten von Caching in Rails implementieren kannst, z. B. Fragment Caching und Russian Doll Caching. Außerdem zeigen wir dir, wie du Cache-Abhängigkeiten verwaltest, Cache-Speicher auswählst und Best Practices für den effektiven Einsatz von Caching in einer Rails-Anwendung umreißt.
Dieser Artikel setzt voraus, dass du dich mit Ruby on Rails auskennst, Rails Version 6 oder höher verwendest und mit Rails Views vertraut bist. Die Codebeispiele zeigen, wie du Caching in neuen oder bestehenden View-Templates einsetzen kannst.
Arten von Ruby on Rails-Caching
In Ruby on Rails-Anwendungen gibt es verschiedene Arten des Cachings, die von der Ebene und Granularität des zu cachenden Inhalts abhängen. Die wichtigsten Arten, die in modernen Rails-Anwendungen verwendet werden, sind:
- Fragment-Caching: Hier werden Teile einer Webseite gecached, die sich nicht häufig ändern, wie Kopf- und Fußzeilen, Seitenleisten oder statische Inhalte. Das Caching von Fragmenten reduziert die Anzahl der Teilbereiche oder Komponenten, die bei jeder Anfrage gerendert werden.
- Russian-Doll-Caching: Caching verschachtelter Fragmente einer Webseite, die voneinander abhängen, wie z. B. Sammlungen und Verknüpfungen. Das Russian-Doll-Caching verhindert unnötige Datenbankabfragen und erleichtert die Wiederverwendung von unveränderten Fragmenten im Cache.
Zwei weitere Arten des Cachings waren früher Teil von Ruby on Rails, sind aber jetzt als separate Gems verfügbar:
- Seiten-Caching: Zwischenspeichern ganzer Webseiten als statische Dateien auf dem Server, wobei der gesamte Lebenszyklus des Seiten-Renderings umgangen wird
- Action-Caching: Zwischenspeichern der Ausgabe ganzer Controller-Aktionen. Es ähnelt dem Seiten-Caching, ermöglicht dir aber die Anwendung von Filtern wie der Authentifizierung.
Seiten- und Aktionscaching werden nur selten verwendet und für die meisten Anwendungsfälle in modernen Rails-Anwendungen nicht mehr empfohlen.
Fragment-Caching in Ruby on Rails
Mit dem Fragment-Caching kannst du Teile einer Seite zwischenspeichern, die sich nur selten ändern. Zum Beispiel könnte eine Seite, die eine Liste von Produkten mit den dazugehörigen Preisen und Bewertungen anzeigt, Details zwischenspeichern, die sich wahrscheinlich nicht ändern werden.
Gleichzeitig kann Rails dynamische Teile der Seite – wie Kommentare oder Bewertungen – bei jedem Laden der Seite neu darstellen. Das Zwischenspeichern von Fragmenten ist weniger nützlich, wenn sich die zugrunde liegenden Daten einer Ansicht häufig ändern, da der Cache häufig aktualisiert werden muss.
Als einfachste Art des Cachings in Rails sollte Fragment-Caching deine erste Wahl sein, wenn du deine Anwendung mit Caching ausstattest, um die Leistung zu verbessern.
Um Fragment-Caching in Rails zu nutzen, verwende die Hilfsmethode cache
in deinen Views. Schreibe zum Beispiel den folgenden Code, um ein Teilprodukt in deiner Ansicht zwischenzuspeichern:
<% @products.each do |product| %>
<% cache product do %>
<%= render partial: "product", locals: { product: product } %>
<% end %>
<% end %>
Die Hilfsmethode cache
generiert einen Cache-Schlüssel, der auf dem Klassennamen jedes Elements, id
, und dem Zeitstempel von updated_at
basiert (zum Beispiel products/1-20230501000000
). Wenn ein Nutzer das nächste Mal dasselbe Produkt anfordert, holt der cache
-Helfer das zwischengespeicherte Fragment aus dem Cache-Speicher und zeigt es an, ohne das Produkt aus der Datenbank zu lesen.
Du kannst den Cache-Schlüssel auch anpassen, indem du dem Helfer cache
Optionen übergibst. Wenn du z. B. eine Versionsnummer oder einen Zeitstempel in deinen Cache-Schlüssel einfügen möchtest, schreibe etwas wie das Folgende:
<% @products.each do |product| %>
<% cache [product, "v1"] do %>
<%= render partial: "product", locals: { product: product } %>
<% end %>
<% end %>
Alternativ kannst du auch eine Ablaufzeit festlegen:
<% @products.each do |product| %>
<% cache product, expires_in: 1.hour do %>
<%= render partial: "product", locals: { product: product } %>
<% end %>
<% end %>
Im ersten Beispiel wird v1
an den Cache-Schlüssel angehängt (zum Beispiel products/1-v1
). Das ist nützlich, um den Cache ungültig zu machen, wenn du die Teilvorlage oder das Layout änderst. Das zweite Beispiel legt eine Verfallszeit für den Cache-Eintrag fest (1 Stunde), die dabei hilft, veraltete Daten ablaufen zu lassen.
Russian-Doll-Caching in Ruby on Rails
Russian-Doll-Caching ist eine leistungsstarke Caching-Strategie in Ruby on Rails, die die Leistung deiner Anwendung optimiert, indem sie Caches ineinander verschachtelt. Es nutzt das Rails-Fragment-Caching und Cache-Abhängigkeiten, um redundante Arbeit zu minimieren und die Ladezeiten zu verbessern.
In einer typischen Rails-Anwendung renderst du oft eine Sammlung von Elementen, von denen jedes mehrere untergeordnete Komponenten hat. Wenn du ein einzelnes Element aktualisierst, solltest du vermeiden, die gesamte Sammlung oder alle nicht betroffenen Elemente neu zu rendern. Verwende Russian-Doll-Caching, wenn du mit hierarchischen oder verschachtelten Datenstrukturen arbeitest, besonders wenn die verschachtelten Komponenten ihre eigenen Daten haben, die sich unabhängig voneinander ändern können.
Der Nachteil von Russian-Doll-Caching ist, dass es die Komplexität erhöht. Du musst die Beziehungen zwischen den verschachtelten Ebenen der Elemente, die du zwischenspeichern willst, verstehen, um sicherzustellen, dass du die richtigen Elemente zwischenspeicherst. In manchen Fällen musst du Assoziationen zu deinen Active Record-Modellen hinzufügen, damit Rails die Beziehungen zwischen den zwischengespeicherten Datenelementen ableiten kann.
Wie beim normalen Fragment-Caching wird auch beim Russian-Doll-Caching die Hilfsmethode cache
verwendet. Wenn du zum Beispiel eine Kategorie mit ihren Unterkategorien und Produkten in deiner Ansicht zwischenspeichern möchtest, schreibst du etwas wie folgendes:
<% @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 %>
Der cache
-Helfer speichert jede verschachtelte Ebene separat im Cache-Speicher. Wenn die gleiche Kategorie das nächste Mal angefordert wird, holt sie ihr zwischengespeichertes Fragment aus dem Cache-Speicher und zeigt es an, ohne es erneut zu rendern.
Wenn sich jedoch die Details einer Unterkategorie oder eines Produkts ändern – z. B. der Name oder die Beschreibung -, wird das zwischengespeicherte Fragment ungültig und mit den aktualisierten Daten neu gerendert. Das Russian-Doll-Caching sorgt dafür, dass du nicht eine ganze Kategorie ungültig machen musst, wenn sich eine einzelne Unterkategorie oder ein Produkt ändert.
Cache-Abhängigkeitsmanagement in Ruby on Rails
Cache-Abhängigkeiten sind Beziehungen zwischen zwischengespeicherten Daten und den zugrundeliegenden Quellen, deren Verwaltung kompliziert sein kann. Wenn sich die Quelldaten ändern, sollten alle zugehörigen gecachten Daten ablaufen.
Rails kann Zeitstempel verwenden, um die meisten Cache-Abhängigkeiten automatisch zu verwalten. Jedes Active Record-Modell hat die Attribute created_at
und updated_at
, die angeben, wann der Cache den Datensatz erstellt oder zuletzt aktualisiert hat. Um sicherzustellen, dass Rails das Caching automatisch verwalten kann, definierst du die Beziehungen deiner Active Record-Modelle wie folgt:
class Product < ApplicationRecord
belongs_to :category
end
class Category < ApplicationRecord
has_many :products
end
In diesem Beispiel:
- Wenn du einen Produktdatensatz aktualisierst (z. B. indem du den Preis änderst), ändert sich der Zeitstempel
updated_at
automatisch. - Wenn du diesen Zeitstempel als Teil deines Cache-Schlüssels verwendest (z. B.
products/1-20230504000000
), wird dadurch auch dein gecachtes Fragment automatisch ungültig. - Um das zwischengespeicherte Fragment deiner Kategorie ungültig zu machen, wenn du einen Produktdatensatz aktualisierst – z. B. weil er aggregierte Daten wie den Durchschnittspreis anzeigt -, verwende die Methode
touch
in deinem Controller (@product.category.touch
) oder füge eine Optiontouch
in deiner Modellzuordnung hinzu (belongs_to :category touch: true
).
Ein weiterer Mechanismus zur Verwaltung von Cache-Abhängigkeiten ist die Verwendung von Low-Level-Caching-Methoden – wie fetch
und write
– direkt in deinen Modellen oder Controllern. Mit diesen Methoden kannst du beliebige Daten oder Inhalte in deinem Cache-Speicher mit benutzerdefinierten Schlüsseln und Optionen speichern. Ein Beispiel:
class Product < ApplicationRecord
def self.average_price
Rails.cache.fetch("products/average_price", expires_in: 1.hour) do
average(:price)
end
end
end
In diesem Beispiel wird gezeigt, wie du berechnete Daten – z. B. den Durchschnittspreis aller Produkte – mit der Methode fetch
mit einem benutzerdefinierten Schlüssel (products/average_price
) und einer Ablaufoption (expires_in: 1.hour
) eine Stunde lang zwischenspeichern kannst.
Die Methode fetch
versucht zunächst, die Daten aus dem Cache-Speicher zu lesen. Wenn sie die Daten nicht finden kann oder die Daten abgelaufen sind, führt sie den Block aus und speichert das Ergebnis im Cache-Speicher.
Um einen Cache-Eintrag vor Ablauf der Frist manuell zu deaktivieren, verwendest du die Methode write
mit der Option force
:
Rails.cache.write("products/average_price", Product.average(:price), force: true))
Cache-Speicher und Backends in Ruby on Rails
In Rails kannst du verschiedene Cache-Speicher oder Backends auswählen, um deine gecachten Daten und Inhalte zu speichern. Der Rails-Cache-Speicher ist eine Abstraktionsschicht, die eine gemeinsame Schnittstelle für die Interaktion mit verschiedenen Speichersystemen bietet. Ein Cache-Backend implementiert die Cache-Speicher-Schnittstelle für ein bestimmtes Speichersystem.
Rails unterstützt von Haus aus mehrere Arten von Cache-Speichern oder Backends, die im Folgenden näher beschrieben werden.
Memory Store
Der Memory Store verwendet einen In-Memory-Hash als Cache-Speicher. Er ist schnell und einfach, hat aber eine begrenzte Kapazität und Persistenz. Dieser Cache-Speicher ist für Entwicklungs- und Testumgebungen oder kleine, einfache Anwendungen geeignet.
Disk Store
Der Disk Store verwendet Dateien auf der Festplatte als Cache-Speicher. Er ist die langsamste Cache-Option in Rails, verfügt aber über eine große Kapazität und Persistenz. Disk Store eignet sich für Anwendungen, die große Datenmengen zwischenspeichern müssen und nicht die maximale Leistung benötigen.
Redis
Der Redis-Speicher verwendet eine Redis-Instanz für den Cache-Speicher. Redis ist ein In-Memory-Datenspeicher, der verschiedene Datentypen unterstützt. Er ist zwar schnell und flexibel, erfordert aber einen eigenen Server und eine eigene Konfiguration. Er ist für Anwendungen geeignet, die komplexe oder dynamische Daten zwischenspeichern müssen, die sich häufig ändern. Redis ist die ideale Wahl, wenn du Rails-Anwendungen in der Cloud laufen lässt, denn einige Hosting-Anbieter, darunter Kinsta, bieten Redis als persistenten Objekt-Cache an.
Memcached
Der Memcached-Store verwendet eine Memcached-Instanz für die Cache-Speicherung. Memcached ist ein In-Memory-Schlüsselwertspeicher, der einfache Datentypen und Funktionen unterstützt. Er ist schnell und skalierbar, aber wie Redis erfordert er einen eigenen Server und eine eigene Konfiguration. Dieser Speicher ist für Anwendungen geeignet, die einfache oder statische Daten zwischenspeichern müssen, die häufig aktualisiert werden.
Du kannst deinen Cache-Speicher in deinen Rails-Umgebungsdateien (zum Beispiel config/environments/development.rb) mit der Option config.cache_store
konfigurieren. Hier erfährst du, wie du die eingebauten Caching-Methoden von Rails verwendest:
# 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 solltest nur einen config.cache_store
Aufruf pro Umgebungsdatei haben. Wenn du mehr als einen hast, verwendet der Cache-Speicher nur den letzten.
Jeder Cache-Speicher hat einzigartige Vor- und Nachteile, die von den Bedürfnissen und Vorlieben deiner Anwendung abhängen. Wähle den Speicher, der am besten zu deinem Anwendungsfall und deiner Erfahrung passt.
Best Practices für Ruby on Rails Caching
Der Einsatz von Caching in deiner Rails-Anwendung kann ihre Leistung und Skalierbarkeit erheblich steigern, vor allem wenn du die folgenden Best Practices umsetzt:
- Cache selektiv: Cache nur Daten, auf die häufig zugegriffen wird, die teuer zu generieren sind oder die selten aktualisiert werden. Vermeide übermäßiges Caching, um übermäßigen Speicherverbrauch, das Risiko veralteter Daten und Leistungseinbußen zu vermeiden.
- Cache-Einträge ablaufen lassen: Verhindere veraltete Daten, indem du ungültige oder irrelevante Einträge verfallen lässt. Verwende Zeitstempel, Ablaufoptionen oder die manuelle Ungültigkeitserklärung.
- Optimiere die Cache-Leistung: Wähle den Cache-Speicher, der zu den Anforderungen deiner Anwendung passt, und passe seine Parameter – wie Größe, Komprimierung oder Serialisierung – an, um eine optimale Leistung zu erzielen.
- Überwache und teste die Auswirkungen des Caches: Beurteile das Cache-Verhalten – wie Hit-Rate, Miss-Rate und Latenz – und bewerte ihre jeweiligen Auswirkungen auf die Leistung (Antwortzeit, Durchsatz, Ressourcenverbrauch). Verwende Tools wie New Relic, Rails-Logs, ActiveSupport-Benachrichtigungen oder den Rack Mini Profiler.
Zusammenfassung
Das Caching von Ruby on Rails verbessert die Leistung und Skalierbarkeit von Anwendungen, indem es häufig genutzte Daten oder Inhalte effizient speichert und wiederverwendet. Mit einem tieferen Verständnis der Caching-Techniken bist du besser gerüstet, um deinen Nutzern schnellere Rails-Anwendungen zu liefern.
Wenn du deine optimierte Rails-Anwendung bereitstellen willst, kannst du die Anwenndungs-Hosting-Plattform von Kinsta wenden. Mit einem Hobby-Tier-Konto kannst du kostenlos loslegen und die Plattform mit diesem Ruby on Rails-Schnellstart-Beispiel erkunden.
Schreibe einen Kommentar