OPcache, Redis, Varnish und CDN sinnvoll kombinieren
Wer OPcache, Redis, Varnish und ein CDN ohne klare Rollenverteilung betreibt, produziert widersprüchliche Ladezeiten und schwer reproduzierbare Fehler durch veraltete Daten. Dieser Artikel zeigt, wie jede Schicht ein eigenes Problem löst, wie Invalidierung über Tags und TTL zuverlässig funktioniert und wie sich Stampedes durch Sperren und vorausschauende Erneuerung vermeiden lassen.
Inhaltsverzeichnis
- 1. Die Cache-Layer-Hierarchie: OPcache, Objekt-Cache, FPC und CDN im Überblick
- 2. OPcache: Bytecode-Caching als unterste Schicht richtig konfigurieren
- 3. Objekt-/Applikationscache mit Redis: Konfiguration und Einsatzgebiete
- 4. Full Page Cache (Varnish): ganze HTML-Antworten cachen
- 5. CDN Edge Cache: die äußerste Schicht vor dem Ursprung
- 6. Cache-Invalidierung über Schichten hinweg: Tags vs. TTL-Strategie
- 7. Cache-Busting für statische Assets und Versionierung
- 8. Cache-Stampede vermeiden: Locking, Request Coalescing und probabilistische Erneuerung
- 9. Die Cache-Layer im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Die Cache-Layer-Hierarchie: OPcache, Objekt-Cache, FPC und CDN im Überblick
Jede Cache-Schicht in einem Magento-Stack löst ein anderes Problem und arbeitet auf einer anderen Ebene des Request-Lebenszyklus. OPcache sitzt am tiefsten: Es cacht kompilierten PHP-Bytecode im Shared Memory und verhindert, dass jeder Request Hunderte PHP-Dateien erneut parsen und kompilieren muss - ein Schritt, der bei einem durchschnittlichen Magento-Request ohne OPcache 20 bis 40 Millisekunden allein für Parsing und Kompilierung kostet. Darüber liegt der Objekt-/Applikationscache mit Redis, der berechnete Ergebnisse wie EAV-Attribute, Layout-XML, Blockinhalte und Konfigurationsdaten zwischenspeichert und so wiederholte Datenbankabfragen erspart.
Noch eine Ebene höher liegt der Full Page Cache mit Varnish, der komplette gerenderte HTML-Antworten vorhält und damit den gesamten PHP-Prozess für wiederkehrende Requests überspringt. Ganz außen sitzt das CDN als Edge-Cache, der Antworten geografisch verteilt an Points of Presence nahe am Nutzer vorhält und verhindert, dass der Origin-Server überhaupt erreicht wird. Die vier Schichten bauen aufeinander auf: Ein Cache-Miss in einer äußeren Schicht fällt auf die nächstinnere zurück, bis im schlimmsten Fall jede Anfrage bei der Datenbank landet.
2. OPcache: Bytecode-Caching als unterste Schicht richtig konfigurieren
OPcache ist die einzige Cache-Schicht, die praktisch immer aktiv sein sollte, da sie ohne Konfigurationsaufwand messbare Gewinne bringt. Entscheidend ist opcache.memory_consumption: Magento mit Vendor-Verzeichnis und generierten Klassen umfasst oft über 60.000 PHP-Dateien, weshalb die Standardeinstellung von 128 MB schnell an ihre Grenzen stößt und zu ständigem Cache-Churn führt, bei dem OPcache Einträge verwirft, obwohl noch Speicher gebraucht würde. 256 MB und opcache.max_accelerated_files von mindestens 130.000 sind für produktive Magento-Installationen ein realistischer Ausgangswert.
opcache.validate_timestamps=0 deaktiviert die Prüfung, ob eine PHP-Datei seit dem letzten Kompilieren verändert wurde, und spart pro Request einen Dateisystem-Stat-Aufruf pro Datei - in Produktion sinnvoll, aber nur in Kombination mit einem Deploy-Prozess, der OPcache nach jedem Release explizit über opcache_reset() oder einen PHP-FPM-Reload zurücksetzt. Ohne diesen Schritt laufen alte Server-Prozesse tagelang mit veraltetem Bytecode weiter. opcache.preload lädt beim PHP-FPM-Start eine definierte Klassenliste dauerhaft in den Speicher; Magento generiert dafür seit 2.4.x über bin/magento generate:preload eine passende preload.php, die typische Klassenauflösungskosten fast vollständig eliminiert.
; php.ini / opcache.ini - Production OPcache configuration for Magento 2
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=130000
opcache.validate_timestamps=0
opcache.revalidate_freq=0
opcache.save_comments=1
opcache.fast_shutdown=1
opcache.preload=/var/www/html/generated/preload.php
opcache.preload_user=www-data
3. Objekt-/Applikationscache mit Redis: Konfiguration und Einsatzgebiete
Der Objekt-Cache speichert das Ergebnis teurer PHP-Operationen: kompilierte Layout-XML, gerenderte Blockfragmente, Konfigurationsbäume und EAV-Attributmetadaten. Ohne ihn würde Magento diese Strukturen bei jedem einzelnen Request neu aus der Datenbank zusammensetzen. Redis ist dafür die empfohlene Backend-Wahl gegenüber Dateisystem oder Memcached, weil es atomare Operationen, Tag-basierte Invalidierung über Sets und eine deutlich höhere Durchsatzrate bei vielen kleinen Schlüsseln bietet. Wichtig ist, den Objekt-Cache, den Session-Storage und das optionale Redis-Backend für Varnish auf getrennte Redis-Datenbanken oder sogar getrennte Instanzen zu legen, damit ein Speicherengpass in einem Bereich nicht die anderen mitreißt.
Die maxmemory-policy entscheidet, was passiert, wenn Redis sein Speicherlimit erreicht: allkeys-lru verwirft die am längsten nicht genutzten Schlüssel und ist für den Objekt-Cache fast immer richtig, da verlorene Einträge lediglich zu einem Cache-Miss und einer erneuten Berechnung führen, nicht zu Datenverlust. Wird stattdessen noeviction verwendet, wirft Redis bei vollem Speicher Schreibfehler, was zu sichtbaren Fehlern im Shop führen kann. compress_data in der Backend-Konfiguration reduziert den Speicherbedarf großer Layout- und Blockeinträge spürbar, kostet dafür CPU-Zeit beim Lesen und Schreiben.
<?php
// app/etc/env.php - Redis-backed cache configuration for Magento 2
return [
'cache' => [
'frontend' => [
'default' => [
'backend' => 'Cm_Cache_Backend_Redis',
'backend_options' => [
'server' => '127.0.0.1',
'port' => '6379',
'database' => '0',
'compress_data' => '1',
'compression_lib' => 'gzip',
],
],
'page_cache' => [
'backend' => 'Cm_Cache_Backend_Redis',
'backend_options' => [
'server' => '127.0.0.1',
'port' => '6379',
'database' => '1',
'compress_data' => '0',
],
],
],
],
];
4. Full Page Cache (Varnish): ganze HTML-Antworten cachen
Der Full Page Cache speichert die komplette gerenderte HTML-Antwort einer Seite und überspringt damit nicht nur Datenbankzugriffe, sondern den gesamten PHP-Prozess inklusive Objekt-Cache-Lookups. Für eine Kategorie- oder Produktseite mit aktivem Cache liefert Varnish die Antwort in 5 bis 15 Millisekunden aus, verglichen mit 200 bis 800 Millisekunden für denselben Request durch den vollen Magento-Stack. Die Herausforderung liegt in personalisierten Inhalten wie Warenkorb-Mini-Ansicht, Login-Status oder zuletzt gesehenen Produkten, die pro Nutzer unterschiedlich sind und nicht im gecachten HTML landen dürfen.
Magento löst das über Hole Punching mit Edge Side Includes: Der Großteil der Seite bleibt cachefähig, während private Blöcke als separate ESI-Fragmente markiert werden, die Varnish bei jedem Request einzeln nachlädt und in die gecachte Antwort einfügt. Jede Antwort trägt außerdem einen X-Magento-Tags-Header mit allen Entity-IDs, die im HTML verarbeitet wurden. Dieser Header ist die Grundlage für die spätere gezielte Invalidierung, ohne die gesamte Seite ungültig zu machen, wenn sich nur ein einzelnes referenziertes Produkt ändert.
// varnish.vcl - propagate Magento cache tags and support tag-based bans
sub vcl_backend_response {
// Keep the tag header so it can be used for targeted bans later
set beresp.http.X-Magento-Tags = beresp.http.X-Magento-Tags;
set beresp.ttl = 86400s;
set beresp.grace = 3h;
}
sub vcl_recv {
if (req.method == "BAN") {
// Ban all objects whose X-Magento-Tags header matches the given tag
ban("obj.http.X-Magento-Tags ~ " + req.http.X-Magento-Tags-Pattern);
return (synth(200, "Banned"));
}
}
5. CDN Edge Cache: die äußerste Schicht vor dem Ursprung
Ein CDN repliziert Cache-Inhalte über geografisch verteilte Points of Presence und beantwortet Anfragen dort, wo der Nutzer physisch näher ist, statt jede Anfrage bis zum Origin-Server durchzureichen. Für einen Besucher in Australien, dessen Origin-Server in Frankfurt steht, reduziert ein Edge-Hit die Netzwerklatenz von 250 bis 300 Millisekunden auf 10 bis 40 Millisekunden allein durch die geografische Nähe, unabhängig davon, wie schnell Varnish oder Redis am Ursprung arbeiten. Für statische Assets wie CSS-, JS- und Bilddateien ist das CDN die mit Abstand wirkungsvollste Cache-Schicht, weil diese Inhalte sich selten ändern und lange TTLs von Tagen oder Wochen vertragen.
Bei dynamischem, cachefähigem HTML ist das CDN komplexer zu betreiben, weil es Magentos Cache-Tag-System nicht nativ versteht. Die meisten CDN-Anbieter bieten stattdessen eigene Mechanismen wie Surrogate-Keys oder Cache-Tags über eine Purge-API an, die separat von Varnish gepflegt werden müssen. Ein häufiger Fehler ist, dieselbe TTL-Strategie wie für Varnish zu verwenden, obwohl Purge-Vorgänge über die CDN-API oft Sekunden bis wenige Minuten brauchen, um sich über alle Edge-Knoten zu propagieren. Für preissensible Inhalte ist deshalb eine kürzere CDN-TTL als FPC-TTL oft die sicherere Wahl.
6. Cache-Invalidierung über Schichten hinweg: Tags vs. TTL-Strategie
Tag-basierte Invalidierung markiert jeden Cache-Eintrag mit den IDs der Entitäten, die zu seinem Inhalt beigetragen haben: Ein Produkt taucht in seiner eigenen Detailseite, in Kategorielisten, in Cross-Sell-Blöcken und im Suchindex auf, entsprechend erhält der Cache-Eintrag mehrere Tags. Wird das Produkt gespeichert, feuert Magento ein Event, das gezielt nur Einträge mit passenden Tags entfernt, während der Rest des Caches warm bleibt. Das ist präzise, aber nur so gut wie die Vollständigkeit der Tags: Fehlt ein Tag an einer Stelle, bleibt dort veralteter Inhalt sichtbar, ohne dass ein Fehler auftritt.
TTL-basierte Invalidierung ist das grobe Gegenstück: Ein Eintrag verfällt nach einer festen Zeitspanne, unabhängig davon, ob sich der zugrunde liegende Inhalt geändert hat. CDNs nutzen TTL meist als alleinige Strategie, weil sie keinen Einblick in Magentos Tag-System haben. In der Praxis funktioniert eine kombinierte Strategie am besten: präzise Tag-Invalidierung auf der Varnish-Ebene für sofortige Korrektheit, kombiniert mit einer kurzen TTL von wenigen Minuten auf der CDN-Ebene als Sicherheitsnetz, falls eine Purge-Anfrage verloren geht oder verzögert ankommt.
<?php
// Invalidate only the cache entries tagged with this product, not the whole FPC
final class ProductCacheInvalidator
{
public function __construct(
private readonly \Magento\Framework\App\CacheInterface $cache,
private readonly \Magento\Framework\Indexer\CacheContext $cacheContext,
) {
}
/**
* Clear cache entries tagged with the given product IDs after save.
*
* @param int[] $productIds
* @return void
*/
public function invalidateByProductIds(array $productIds): void
{
$tags = array_map(
static fn (int $id): string => \Magento\Catalog\Model\Product::CACHE_TAG . '_' . $id,
$productIds
);
// Only entries carrying these tags are purged, everything else stays warm
$this->cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG, $tags);
}
}
7. Cache-Busting für statische Assets und Versionierung
Statische Assets wie CSS- und JS-Bundles sollten mit möglichst langer TTL ausgeliefert werden, idealerweise ein Jahr, da sie sich für den Browser und das CDN wie unveränderliche Dateien verhalten dürfen. Das setzt voraus, dass sich die URL bei jeder inhaltlichen Änderung mitändert. Genau das leistet Magentos Static-Content-Versionierung, die bei jedem setup:static-content:deploy einen neuen Versions-Timestamp in den Asset-Pfad schreibt. Ändert sich eine einzelne CSS-Datei, ändert sich dadurch die komplette Pfadstruktur, und alte gecachte Versionen werden nie erneut angefragt, sondern verwaist im Cache liegen gelassen, bis ihre TTL abläuft.
Ein kritischer Fehler beim Deploy ist, den Versionswechsel nicht atomar durchzuführen: Wird HTML mit neuen Asset-Pfaden bereits an Nutzer ausgeliefert, während die zugehörigen Dateien auf einem Teil der Server oder CDN-Knoten noch fehlen, entstehen kaputte Seiten mit 404-Fehlern für CSS und JS. Deploy-Strategien wie Blue-Green-Deployment oder ein vorgeschaltetes CDN-Warmup, das neue Assets vor dem eigentlichen Cutover an alle Edge-Knoten verteilt, verhindern dieses Zeitfenster zuverlässig.
8. Cache-Stampede vermeiden: Locking, Request Coalescing und probabilistische Erneuerung
Ein Cache-Stampede entsteht, wenn ein populärer Cache-Eintrag gleichzeitig für viele parallele Requests abläuft oder durch einen Cache-Flush ungültig wird: Statt eines einzelnen Requests, der den Wert neu berechnet, treffen plötzlich hunderte Requests gleichzeitig auf einen Miss und lösen alle parallel dieselbe teure Datenbankabfrage oder Rendering-Operation aus. Auf einem stark frequentierten Magento-Shop kann ein einzelner Cache-Flush zur Hauptlast-Zeit die Datenbank für Sekunden bis Minuten überlasten, obwohl der eigentliche Cache-Miss nur einmal echte Arbeit erfordern sollte.
Locking löst das Problem, indem nur der erste Request, der einen Miss feststellt, eine kurze Sperre setzt und die Neuberechnung übernimmt, während alle weiteren Requests entweder kurz warten oder einen zuletzt bekannten, leicht veralteten Wert ausliefern - dieses Muster heißt Request Coalescing. In Redis lässt sich eine solche Sperre einfach mit SET NX EX umsetzen: Der erste Prozess erhält den Schlüssel, alle anderen scheitern am NX-Flag und fallen auf einen kurzen Retry oder Stale-Wert zurück. Wichtig ist eine niedrige Lock-TTL von wenigen Sekunden, damit ein abgestürzter Prozess die Sperre nicht dauerhaft blockiert.
Probabilistische Early Expiration, bekannt als XFetch-Algorithmus, geht einen Schritt weiter und verhindert Stampedes bereits präventiv: Statt strikt bei Ablauf der TTL neu zu berechnen, erhöht sich mit zunehmender Nähe zum Ablaufzeitpunkt die Wahrscheinlichkeit, dass ein einzelner Request die Neuberechnung vorzeitig anstößt, während alle anderen weiterhin den noch gültigen Wert erhalten. Kombiniert mit einem kleinen zufälligen Jitter auf die TTL selbst verhindert das zusätzlich, dass viele verwandte Schlüssel exakt gleichzeitig ablaufen und dieselbe Lastspitze gemeinsam auslösen.
<?php
// Prevent cache stampede with a short-lived Redis lock around expensive regeneration
final class StampedeSafeCache
{
public function __construct(private readonly \Redis $redis)
{
}
/**
* Fetch a value from cache, regenerating it under a lock on miss.
*
* @param string $key
* @param callable $regenerate
* @param int $ttl
* @return string
*/
public function get(string $key, callable $regenerate, int $ttl = 300): string
{
$value = $this->redis->get($key);
if ($value !== false) {
return $value;
}
$lockKey = $key . ':lock';
// Only one process wins the lock; everyone else waits briefly and retries
if ($this->redis->set($lockKey, '1', ['NX', 'EX' => 10])) {
$value = $regenerate();
$this->redis->setex($key, $ttl, $value);
$this->redis->del($lockKey);
return $value;
}
usleep(100_000);
return $this->redis->get($key) ?: $regenerate();
}
}
9. Die Cache-Layer im direkten Vergleich
Jede der vier Cache-Schichten hat ein eigenes Profil aus Inhalt, Latenz und Invalidierungsmechanismus. Die folgende Übersicht fasst die wichtigsten Unterschiede zusammen.
| Schicht | Was wird gecacht | Typische Hit-Latenz | Größtes Risiko | Invalidierungstrigger |
|---|---|---|---|---|
| OPcache | Kompilierter PHP-Bytecode | < 1 ms | Veralteter Code nach Deploy | opcache_reset() / Deploy-Hook |
| Objekt-Cache (Redis) | EAV-Daten, Layout, Blöcke, Konfiguration | 0,5 bis 2 ms | Eviction durch falsche maxmemory-policy | Tag-Clean bei Entity-Save |
| Full Page Cache (Varnish) | Komplette HTML-Antworten | 5 bis 15 ms | Personalisierte Inhalte im Cache | BAN/PURGE via X-Magento-Tags |
| CDN Edge Cache | Statische Assets & cachefähiges HTML | 10 bis 40 ms | Verzögerte Purge-Propagation | TTL-Ablauf oder API-Purge |
In der Praxis wirken die vier Schichten nur zusammen zuverlässig: Fällt eine Schicht durch falsche Konfiguration aus, springt automatisch die nächstinnere ein und übernimmt zusätzliche Last, unbemerkt, bis auch diese an ihre Grenzen stößt. Wer OPcache, Redis, Varnish und CDN als zusammenhängendes System statt als isolierte Einzelmaßnahmen betrachtet, vermeidet genau dieses schleichende Versagen unter Last.
Mironsoft
Caching-Architektur, Redis/OPcache-Tuning und Stampede-Schutz für Magento-Shops
Cache-Layer professionell schichten?
Wir analysieren OPcache, Redis, Varnish und CDN eures Magento-Shops, identifizieren Invalidierungslücken und Stampede-Risiken und setzen eine belastbare Cache-Architektur um, die auch unter Lastspitzen stabil bleibt.
Caching-Architektur-Review
Analyse der gesamten Cache-Hierarchie und Invalidierungslogik
Redis/OPcache-Tuning
Speicherlimits, Eviction-Policies und Preloading richtig konfigurieren
Stampede-Schutz
Locking, Request Coalescing und probabilistische Erneuerung implementieren
10. Zusammenfassung
Server-seitiges Caching in Magento funktioniert nur als bewusst geschichtetes System: OPcache eliminiert Kompilierungskosten auf jeder einzelnen Anfrage, der Objekt-Cache mit Redis erspart wiederholte Datenbankabfragen und Berechnungen, der Full Page Cache überspringt den PHP-Prozess für wiederkehrende Seitenaufrufe komplett, und das CDN reduziert Netzwerklatenz durch geografische Nähe zum Nutzer. Jede Schicht schützt vor einer anderen Kostenquelle, und ein Ausfall einer Schicht verschiebt die Last unbemerkt auf die nächstinnere, bis das System insgesamt langsamer wird.
Invalidierung ist dabei der eigentliche Schwierigkeitsgrad: Tag-basierte Ansätze liefern Präzision, sind aber nur so vollständig wie ihre Tag-Abdeckung, während TTL-basierte Ansätze robust, aber grob sind. Cache-Stampedes durch Locking, Request Coalescing und probabilistische Early Expiration abzufangen, verhindert, dass ein einzelner Cache-Flush zur Hauptlast-Zeit die Datenbank überlastet. Wer diese Bausteine bewusst kombiniert statt einzeln zu betrachten, baut eine Cache-Architektur, die auch unter Lastspitzen vorhersagbar bleibt.
Server-seitige Caching-Layer - Das Wichtigste auf einen Blick
OPcache
256 MB Speicher, validate_timestamps=0 mit Deploy-Reset, Preloading über generate:preload.
Objekt-Cache (Redis)
Separate Datenbanken für Cache, Session und FPC-Backend, allkeys-lru als maxmemory-policy.
Full Page Cache (Varnish)
ESI für personalisierte Blöcke, X-Magento-Tags für präzise Invalidierung.
CDN & Stampede-Schutz
Kurze CDN-TTL als Sicherheitsnetz, Locking und probabilistische Erneuerung gegen Lastspitzen.