granulare Invalidierung statt globalem Flush
Magentos Full Page Cache invalidiert Einträge über Tags wie cat_p_123 oder cat_c_45, die entitätsbezogen, aber standardmäßig nicht store-spezifisch sind. In einer Multi-Store-Installation mit mehreren Websites, Store-Groups und Store-Views bedeutet das: Eine Änderung, die eigentlich nur eine einzige Store-View betrifft, etwa eine store-spezifische Preisregel oder ein individueller Freigabestatus, kann trotzdem Cache-Einträge für alle anderen Stores mit invalidieren, weil sie denselben Tag-Namensraum teilen. Wer viele Stores betreibt, muss deshalb bewusst entscheiden, wie granular Tags aufgebaut werden, ohne dabei in eine Tag-Explosion mit unüberschaubar vielen, kaum wiederverwendeten Tags zu laufen.
Inhaltsverzeichnis
- 1. Warum globale Flush-Operationen bei vielen Stores teuer werden
- 2. Wie Magento Cache-Tags heute aufbaut
- 3. Store-View-spezifische Tag-Erweiterung implementieren
- 4. Tag-Explosion vermeiden: die Grenze der Granularität
- 5. Selective Flush statt globalem Cache-Clean bei Deploy und Wartung
- 6. Monitoring der Tag-Invalidierungs-Rate im laufenden Betrieb
- 7. Die zusätzliche Dimension: Customer-Group-abhängige Preise
- 8. Die Tag-Strategie unter realistischer Last testen
- 9. Grenzen und Trade-offs der granularen Tag-Strategie
- 10. Zusammenfassung
- 11. FAQ
1. Warum globale Flush-Operationen bei vielen Stores teuer werden
In einer Single-Store-Installation ist die klassische Tag-Invalidierung unproblematisch: Ändert sich ein Produkt, wird der zugehörige Tag geleert, und der betroffene, überschaubare Teil des Caches wird neu aufgebaut. Bei einer Installation mit zehn oder mehr Store-Views auf unterschiedlichen Domains oder Sprachen vervielfacht sich die Anzahl der Cache-Einträge pro Entität jedoch entsprechend, weil jede Store-View ihre eigene, store-spezifische HTML-Variante im Cache hält.
Wird ein Tag invalidiert, der store-übergreifend gilt, verlieren plötzlich alle Store-Varianten gleichzeitig ihren Cache-Eintrag, selbst wenn die eigentliche Änderung nur für eine einzige Store-View relevant war, etwa eine store-spezifische Preisänderung durch eine Website-gebundene Sonderaktion. Die Folge sind unnötig viele gleichzeitige Cache-Misses und ein spürbarer Lastanstieg auf Datenbank und Anwendungsserver, wenn viele Besucher kurz nacheinander store-übergreifend neu generierte Seiten anfordern.
2. Wie Magento Cache-Tags heute aufbaut
Magentos Cache-Tags folgen einem entitätsbezogenen Schema: Ein Produkt erzeugt einen Tag wie cat_p_123, eine Kategorie cat_c_45, unabhängig davon, in wie vielen Store-Views oder Websites diese Entität sichtbar ist. Diese Tags werden bei jedem Speichern der Entität über Magentos Indexer- und Cache-Invalidierungslogik automatisch an alle betroffenen Cache-Einträge angehängt, wobei die eigentliche Store-Unterscheidung erst über den X-Magento-Vary-Header und den daraus abgeleiteten Cache-Key erfolgt, nicht über den Tag selbst.
Diese Trennung ist bewusst so gewählt: Der Tag identifiziert die Entität, der Cache-Key identifiziert die konkrete, kontextabhängige Ausprägung. Für die meisten Anwendungsfälle reicht das aus, weil eine Produktänderung ohnehin meist für alle Stores gleichermaßen relevant ist. Bei store-spezifischen Attributen, individuellen Preisen oder website-gebundener Sichtbarkeit entsteht dadurch aber eine unnötig grobe Granularität bei der Invalidierung.
3. Store-View-spezifische Tag-Erweiterung implementieren
Um granularer zu invalidieren, lässt sich der bestehende Tag um die Store-ID erweitern, etwa als zusätzlicher Tag cat_p_123_store_2 neben dem klassischen, store-übergreifenden cat_p_123. Beim Speichern einer Entität wird dann geprüft, ob sich die Änderung tatsächlich auf alle zugeordneten Stores auswirkt oder nur auf eine Teilmenge, etwa weil nur ein store-spezifisches Attribut wie eine lokale Preisüberschreibung geändert wurde. Nur im ersten Fall wird der globale Tag invalidiert, im zweiten Fall ausschließlich die betroffenen store-spezifischen Tags.
Technisch lässt sich das über ein Plugin auf die Cache-Tag-Generierung der relevanten Blöcke oder über einen eigenen Observer auf die Save-Events der jeweiligen Entität umsetzen, der zusätzlich zu den Standard-Tags die store-spezifischen Varianten an die Invalidierungslogik übergibt, ohne die bestehende Magento-Kernlogik zu verändern.
<?php
declare(strict_types=1);
namespace Mironsoft\StoreCacheTags\Plugin;
use Magento\Catalog\Model\Product;
use Magento\Framework\App\CacheInterface;
/**
* Ergänzt store-spezifische Cache-Tags, wenn eine Änderung
* nachweislich nur einzelne Store-Views betrifft.
*/
class AddStoreScopedTags
{
/**
* @param CacheInterface $cache Magento-Cache-Frontend, Redis-gestützt.
*/
public function __construct(private readonly CacheInterface $cache)
{
}
/**
* Invalidiert store-spezifische Tags zusätzlich zum globalen Tag.
*
* @param Product $product Gespeicherte Produktentität.
* @param array $affectedStoreIds Store-IDs mit tatsächlicher Änderung.
* @return void
*/
public function invalidateScopedTags(Product $product, array $affectedStoreIds): void
{
$tags = [];
foreach ($affectedStoreIds as $storeId) {
$tags[] = sprintf('cat_p_%d_store_%d', $product->getId(), $storeId);
}
$this->cache->clean($tags);
}
}
4. Tag-Explosion vermeiden: die Grenze der Granularität
Der naheliegende Fehler bei diesem Ansatz ist, die Granularität zu weit zu treiben: Ein Tag pro Kombination aus Entität, Store-View und Customer-Group würde bei fünfzig Stores und zehn Customer-Groups schnell fünfhundert Tags pro Produkt erzeugen, von denen die meisten nie einzeln invalidiert werden. Redis selbst verwaltet Tags über Set-Strukturen, die jeden zugehörigen Cache-Key referenzieren, sodass eine zu feine Aufteilung nicht nur die Anzahl der Tags selbst, sondern auch den Speicherbedarf dieser internen Set-Strukturen unnötig aufbläht.
Eine sinnvolle Faustregel ist, die Granularität an der tatsächlichen Änderungshäufigkeit auszurichten: Store-spezifische Tags lohnen sich dort, wo Änderungen regelmäßig nur einzelne Stores betreffen, etwa bei lokalisierten Preisen oder store-gebundenen Sonderaktionen. Für Attribute, die praktisch immer store-übergreifend geändert werden, etwa Basisdaten wie Artikelnummer oder Gewicht, bleibt der einfache, globale Tag die bessere Wahl, weil die zusätzliche Granularität dort keinen echten Nutzen bringt.
5. Selective Flush statt globalem Cache-Clean bei Deploy und Wartung
Neben der laufenden Invalidierung durch Entitätsänderungen kommt bei Multi-Store-Setups häufig ein zweites Problem hinzu: Nach einem Deploy oder einer Konfigurationsänderung wird oft reflexartig der komplette Cache über bin/magento cache:flush geleert, obwohl die Änderung nur eine einzelne Website oder Store-Group betraf. Bei vielen Stores bedeutet das einen unnötig breiten Cache-Kaltstart für Bereiche, die von der eigentlichen Änderung gar nicht betroffen waren.
Mit store-spezifischen Tags lässt sich stattdessen ein selektives Flush pro Website oder Store-Group umsetzen, etwa über ein eigenes CLI-Kommando, das gezielt alle Tags einer bestimmten Store-ID sammelt und nur diese über redis-cli oder die Magento-Cache-API löscht, während der Cache aller anderen Stores unangetastet bleibt und weiterhin Hits liefert.
# Alle Cache-Keys eines bestimmten Store-Tag-Namensraums ermitteln und löschen
STORE_ID=2
redis-cli --scan --pattern "*_store_${STORE_ID}*" | while read -r key; do
redis-cli DEL "$key"
done
6. Monitoring der Tag-Invalidierungs-Rate im laufenden Betrieb
Ohne Messung bleibt unklar, ob eine store-spezifische Tag-Strategie tatsächlich die erhoffte Wirkung erzielt. Sinnvoll ist ein Zähler, der bei jeder Invalidierung protokolliert, wie viele Cache-Keys tatsächlich betroffen waren und ob es sich um einen globalen oder einen store-spezifischen Tag handelte. Steigt die Anzahl global invalidierter Keys trotz eingeführter store-spezifischer Tags nicht spürbar, deutet das darauf hin, dass die Unterscheidung zwischen store-weiten und store-spezifischen Änderungen in der Praxis nicht korrekt getroffen wird.
Zusätzlich liefert INFO keyspace in Redis einen groben Überblick über die Gesamtzahl der Keys, während sich über redis-cli --scan --pattern 'cat_p_*_store_*' | wc -l gezielt die Anzahl der store-spezifischen Tag-Varianten ermitteln lässt, um Kardinalität und Wachstum über die Zeit im Blick zu behalten.
# Anzahl store-spezifischer Tags im Vergleich zu globalen Tags
GLOBAL=$(redis-cli --scan --pattern 'cat_p_*' | grep -cv '_store_')
SCOPED=$(redis-cli --scan --pattern 'cat_p_*_store_*' | wc -l)
echo "Global: $GLOBAL, Store-spezifisch: $SCOPED
7. Die zusätzliche Dimension: Customer-Group-abhängige Preise
Store-spezifische Tags lösen das Store-Problem, decken aber nicht automatisch Preisunterschiede zwischen Customer-Groups innerhalb derselben Store-View ab. Ändert sich eine Sonderpreisregel nur für eine bestimmte Kundengruppe, etwa Großhandelskunden, betrifft das zwar keine anderen Stores, wohl aber weiterhin alle Customer-Group-Varianten des Caches innerhalb dieser Store-View, sofern keine zusätzliche Differenzierung eingeführt wird.
In der Praxis lohnt sich eine zusätzliche Customer-Group-Dimension in den Tags meist nur bei Installationen mit sehr wenigen, klar abgegrenzten Kundengruppen und häufigen, gruppenspezifischen Preisänderungen. Bei vielen Kundengruppen sollte die zusätzliche Kardinalität gegen den tatsächlichen Nutzen abgewogen werden, da sich sonst dieselbe Tag-Explosion wie bei zu granularen Store-Tags wiederholt.
8. Die Tag-Strategie unter realistischer Last testen
Vor dem produktiven Einsatz lohnt sich ein Testlauf auf einer Staging-Umgebung mit einer repräsentativen Anzahl an Stores und simulierten Preisänderungen, bei dem gezielt gemessen wird, wie viele Cache-Keys pro Änderung tatsächlich invalidiert werden, verglichen mit dem bisherigen, rein globalen Verhalten. Nur so lässt sich objektiv beurteilen, ob die zusätzliche Komplexität der store-spezifischen Tags den erhofften Effekt auch wirklich erzielt.
Ein einfacher Testaufbau speichert ein Produkt mit einer store-spezifischen Preisänderung, misst vorher und nachher die Anzahl verbleibender, gültiger Cache-Keys für alle anderen Store-Views und vergleicht das Ergebnis mit einem Lauf ohne store-spezifische Tags. Bleibt die Zahl der unbeteiligten, weiterhin gültigen Keys deutlich höher, bestätigt das den praktischen Nutzen der granulareren Strategie.
9. Grenzen und Trade-offs der granularen Tag-Strategie
Jede zusätzliche Tag-Dimension erhöht die Komplexität des Invalidierungscodes und damit auch das Risiko, dass ein übersehener Fall zu veralteten, nicht invalidierten Cache-Einträgen führt, was schwerer zu diagnostizieren ist als eine schlicht zu aggressive, globale Invalidierung. Jede neue Erweiterung sollte deshalb mit automatisierten Tests abgesichert werden, die explizit prüfen, dass store-spezifische Änderungen wirklich nur die betroffenen Stores invalidieren und alle anderen unangetastet lassen.
Für kleine Installationen mit wenigen Stores lohnt sich der Aufwand meist nicht, weil der Unterschied zwischen globaler und store-spezifischer Invalidierung in der Praxis kaum spürbar ist. Erst ab einer zweistelligen Anzahl an Store-Views mit unterschiedlichen, unabhängig voneinander wechselnden Preis- oder Sichtbarkeitsregeln zahlt sich die zusätzliche Granularität durch spürbar weniger unnötige Cache-Misses aus.
| Ansatz | Globaler Tag | Store-spezifischer Tag | Praxisrelevanz |
|---|---|---|---|
| Granularität | Ein Tag pro Entität | Ein Tag pro Entität und Store | Feiner bei store-spezifischen Änderungen |
| Invalidierungsumfang | Alle Stores gleichzeitig | Nur betroffene Stores | Weniger unnötige Cache-Misses |
| Kardinalität | Niedrig, gut skalierbar | Steigt mit Store-Anzahl | Risiko der Tag-Explosion beachten |
| Implementierungsaufwand | Bereits in Magento vorhanden | Zusätzliches Plugin nötig | Nur bei echtem Bedarf einführen |
| Geeignet für | Kleine Installationen | Zehn oder mehr Store-Views | Ab spürbarer Änderungshäufigkeit |
Mironsoft
Cache-Layer-Setup und Magento-Redis-Integration
Magento-Cache, der nicht richtig greift oder falsch konfiguriert ist?
Wir richten Redis als Cache- und Session-Backend für Magento sauber ein, tunen Speicherverbrauch und Eviction-Strategien und sorgen dafür, dass Full Page Cache und Session-Storage zuverlässig zusammenspielen.
Redis-Setup
Cache-, Session- und FPC-Backend produktionsreif für Magento konfigurieren.
Memory-Tuning
Speicherverbrauch und Eviction-Policies auf die tatsächliche Shop-Last abstimmen.
High-Availability-Setup
Redis Sentinel oder Cluster für ausfallsichere Magento-Umgebungen einrichten.
10. Zusammenfassung
Redis-Cache-Tags für Multi-Store-Magento: Das Wichtigste auf einen Blick
Ausgangsproblem
Magentos Cache-Tags sind entitätsbezogen, aber store-übergreifend, wodurch store-spezifische Änderungen unnötig viele Stores gleichzeitig invalidieren.
Lösungsansatz
Store-spezifische Tag-Varianten wie cat_p_123_store_2 ergänzen den globalen Tag und erlauben gezieltes, store-begrenztes Invalidieren.
Grenze der Granularität
Zu viele Tag-Dimensionen führen zu Tag-Explosion; die Granularität sollte sich an der tatsächlichen Änderungshäufigkeit orientieren.
Monitoring
Getrennte Zähler für globale und store-spezifische Invalidierungen sowie regelmäßige Kardinalitätsprüfung per redis-cli SCAN zeigen den tatsächlichen Nutzen.