Die Cache-Invalidierung in PHP besteht nicht darin, eine TTL zu wählen und Antworten in Redis zu speichern. Sie ist eine Konsistenzentscheidung: Es muss festgelegt werden, welche Informationen verzögert sein dürfen, wie lange und was geschehen muss, wenn sich die ursprünglichen Daten ändern. Ein schlecht entworfener Cache kann einen alten Preis anzeigen, Zugriff mit widerrufenen Berechtigungen gewähren oder eine Verfügbarkeit darstellen, die nicht mehr besteht. Ein zu konservativer Cache hingegen verlagert die gesamte Last auf die Datenbank und verfehlt seinen Zweck.
Ausgangspunkt ist, jeden Eintrag als Datensatz mit einem definierten Eigentümer, Lebenszyklus und Risiko zu behandeln. Dadurch können Teams aus Produkt, Business und Technologie vereinbaren, wann eine potenziell veraltete Leseoperation akzeptabel ist und wann der aktuelle Zustand aus der Quelle der Wahrheit abgerufen werden muss.
Klassifizieren Sie Daten, bevor Sie sie cachen

Nicht alle häufig verwendeten Daten sollten gecacht werden, und nicht alle eignen sich für denselben Mechanismus. Bewerten Sie jeden Lesezugriff anhand von vier Kriterien: Volatilität, Auswirkung der Veraltung, Kosten der Abfrage der Quelle und Toleranz gegenüber einem Cache-Ausfall.
- Niedrige Volatilität und geringe Auswirkung: Öffentliche Kataloge, Metadaten oder nicht sensible Konfigurationen erlauben in der Regel TTLs von Minuten oder Stunden, abhängig von ihrem Änderungsprozess.
- Mittlere Volatilität: Produktdetails, Dashboard-Aggregate und Suchergebnisse können gecacht werden, wenn sie invalidiert werden, sobald sich die Datensätze ändern, aus denen sie bestehen.
- Hohe Auswirkung: Berechtigungen, Salden, Limits, Transaktionszustände, Bestand während der Bestellbestätigung und Autorisierungskontrollen erfordern eine konsistente Quelle der Wahrheit oder eine explizite Strategie mit sehr strengen Anforderungen an die Aktualität.
- Aufwendig zu berechnende Daten: Berichte und abgeleitete Zusammenfassungen können einen Cache rechtfertigen, auch wenn sie nicht oft abgefragt werden, müssen jedoch deklarieren, welche Entitäten ihre Invalidierung auslösen.
Es ist sinnvoll, den Präsentations-Cache vom Entscheidungs-Cache zu trennen. Einige Sekunden lang den vorherigen Namen einer Kategorie anzuzeigen, kann akzeptabel sein. Eine alte Richtlinie zur Autorisierung einer Operation zu verwenden, ist es normalerweise nicht. Ziehen Sie für kritische Entscheidungen die autoritative Quelle heran oder speichern Sie Versionen, die vor dem Handeln geprüft werden können.
Definieren Sie Eigentümerschaft, Schlüssel und Aktualitätsverträge
Jeder Eintrag benötigt einen operativen Steckbrief. Er muss die Quelle der Wahrheit, den Schlüssel, die Consumer, die maximale TTL, das Invalidierungsereignis, das Verhalten bei nicht verfügbarem Redis sowie den fachlichen oder technischen Verantwortlichen angeben. Ohne diesen Vertrag vervielfachen sich Schlüssel, und niemand weiß nach einer Änderung, was gelöscht werden muss.
Verwenden Sie vorhersehbare Schlüssel mit ausreichendem Geltungsbereich. Beispielsweise repräsentiert product:42 eine konkrete Entität; tenant:8:product:42 verhindert die Vermischung von Daten zwischen Organisationen; und dashboard:tenant:8:period:current kennzeichnet ein abgeleitetes Ergebnis. Nehmen Sie keine geheimen Daten in Schlüssel auf und verwenden Sie keine instabilen Serialisierungen als Identität.
Es empfiehlt sich außerdem, ein einheitliches Werteformat beizubehalten: Nutzlast, Version oder Erstellungsdatum sowie, wenn relevant, einen Aktualitätsindikator. Ein Consumer sollte nicht annehmen, dass eine gecachte Antwort einer transaktional konsistenten Leseoperation entspricht.
final class ProductCacheKey
{
public static function detail(int $tenantId, int $productId): string
{
return "tenant:{$tenantId}:product:{$productId}:v1";
}
}Das Schema-Suffix ermöglicht es, die Struktur des Werts zu ändern, ohne alle historischen Einträge auffinden und löschen zu müssen. Es ersetzt nicht die Invalidierung der Geschäftsdaten, verringert jedoch Risiken bei einer Weiterentwicklung des Formats.
Wählen Sie das Aktualisierungsmuster nach Art des Lesezugriffs
Cache-aside für wiederverwendbare Lesezugriffe
Bei cache-aside sucht die Anwendung zuerst nach dem Schlüssel; bei einem Cache-Miss fragt sie die Datenbank ab, erstellt den Wert und speichert ihn mit TTL. Das ist einfach und für relativ stabile Lesezugriffe geeignet. Die Grenze ist eindeutig: Nach einem Schreibvorgang muss jemand die betroffenen Einträge löschen oder ersetzen.
Die Invalidierung muss nach dem Bestätigen der Transaktion erfolgen. Ein Löschen vor dem Commit kann dazu führen, dass ein anderer Prozess den Cache noch mit dem alten Wert neu aufbaut. Wenn die Anwendung Ereignisse veröffentlicht, hilft ein Outbox-Pattern dabei, die Änderung in derselben Transaktion zu erfassen und den Invalidierungsauftrag anschließend zuverlässig zuzustellen.
Explizite Aktualisierung und Versionierung
Wenn eine Entität sehr häufig gelesen wird und ihre Änderungen kontrolliert erfolgen, kann der Eintrag nach dem Bestätigen des Schreibvorgangs aktualisiert werden. Dadurch wird der nächste Cache-Miss vermieden. Der Prozess muss jedoch exakt dieselbe Repräsentation erzeugen, die die Leser erwarten; andernfalls sind Invalidieren und Neuaufbau meist weniger riskant.
Die Versionierung von Schlüsseln ist für umfangreiche Abhängigkeiten nützlich. Statt alle Produktlisten einer Organisation zu löschen, wird tenant:8:products:version erhöht, und die Listen nehmen diese Nummer in ihren Schlüssel auf. Die alten Listen laufen von selbst ab. Dieser Ansatz reduziert Massenlöschungen, erfordert jedoch die Kontrolle des Schlüsselwachstums und darf nicht eingesetzt werden, um eine unzureichend verstandene Abhängigkeit zu verbergen.
Kontrollieren Sie Race Conditions und abgeleitete Abhängigkeiten
Die typische Race Condition läuft so ab: Ein Lesezugriff verfehlt den Cache und fragt den alten Wert ab; ein Schreibvorgang wird bestätigt und invalidiert; der erste Lesezugriff endet und speichert den alten Wert erneut. Kombinieren Sie bei sensiblen Daten die Invalidierung mit einer Entitätsversion oder einer kurzen Sperre für den Neuaufbau. Prüfen Sie vor dem Schreiben des berechneten Werts, ob die abgefragte Version weiterhin aktuell ist. Falls nicht, verwerfen Sie das Ergebnis und lesen Sie erneut.
Verteilte Sperren müssen kurz sein, ablaufen und dürfen nicht zu einem einzigen Sperrpunkt werden. Ihre Funktion besteht darin, gleichzeitige Neuaufbauten zu reduzieren, nicht darin, allein die fachliche Konsistenz sicherzustellen. Wenn die Sperre nicht erworben wird, kann eine Option sein, kurz auf den neu aufgebauten Wert zu warten oder einen begrenzten direkten Lesezugriff zu erlauben.
Die Invalidierung nach Abhängigkeiten erfordert ein Inventar. Eine Produktänderung kann dessen Detailansicht, mehrere Listen, Suchergebnisse, Zähler und ein Dashboard betreffen. Eine Rollenänderung kann die effektiven Berechtigungen von Benutzern und abgeleitete Menüs betreffen. Modellieren Sie diese Beziehungen explizit:
- Invalidieren Sie die direkte Entität über ihren Schlüssel.
- Invalidieren oder versionieren Sie die Kollektionen und Aggregate, die von ihr abhängen.
- Berechnen Sie aufwendige Ergebnisse asynchron neu, wenn die Nutzererfahrung dies zulässt.
- Verwechseln Sie nicht das Leeren einer Ansicht mit der Aktualisierung der Quelle der Wahrheit.
Wenn die Beziehung nicht einfach aufzählbar ist, ist ein Versionsraum pro Organisation, Katalog oder Richtlinie in der Regel sicherer, als zu versuchen, alle betroffenen Schlüssel über globale Löschmuster zu ermitteln.
Verwenden Sie TTL, Jitter und Limits, um die Quelle zu schützen
Die TTL ist ein Sicherheitsnetz, nicht der einzige Kohärenzmechanismus. Selbst ein korrekt invalidierter Schlüssel muss ablaufen: Es kann Fehler bei der Ereigniszustellung, Deployment-Fehler oder verwaiste Einträge geben. Wählen Sie die TTL nach den Kosten eines Fehlers, nicht nur nach den Kosten der Abfrage.
Wenden Sie zufälligen Jitter auf die TTL an, damit Tausende gleichzeitig erstellte Schlüssel nicht gleichzeitig ablaufen. Schützen Sie die Quelle außerdem vor einer Lawine von Cache-Misses durch Sperren für den Neuaufbau pro Schlüssel, Parallelitätslimits und Quoten pro Consumer. Für nicht kritische Daten kann ein leicht abgelaufener Wert bereitgestellt werden, während ein einziger Prozess ihn neu berechnet; für Berechtigungen oder entscheidungsrelevante Verfügbarkeit muss diese Technik verworfen oder auf ausdrücklich genehmigte Szenarien beschränkt werden.
Entwerfen Sie die Degradierung bei Redis-Ausfall
Redis ist eine operative Abhängigkeit, nicht die Quelle der Wahrheit. Wenn Redis nicht antwortet, benötigt die Anwendung einen definierten Degradierungsmodus. Bei einem wenig aufwendigen öffentlichen Lesezugriff kann sie die Datenbank direkt mit Zeitlimits abfragen. Bei aufwendigen Abfragen empfiehlt es sich, Lastbegrenzung anzuwenden, Felder zu reduzieren, mit einem vorübergehend nicht verfügbaren Status zu antworten oder eine geeignete Replik zu verwenden, falls die Architektur dies vorsieht.
Verwandeln Sie einen Cache-Ausfall nicht in eine Erschöpfung der Datenbankverbindungen. Definieren Sie kurze Timeouts, Circuit Breaker, Abfragebudgets und Metriken pro Route. Bei kritischen Daten ist es besser, eine Operation abzulehnen, als eine Entscheidung mit Berechtigungen, Salden oder Bestand zu treffen, deren Aktualität nicht gewährleistet werden kann.
Testen und beobachten Sie die Aktualität, nicht nur die Treffer
Eine hohe Trefferquote beweist nicht, dass der Cache korrekt ist. Instrumentieren Sie Treffer, Fehlschläge, Latenz, Lese- und Schreibfehler, verbleibende TTL, Sperren für den Neuaufbau, ausgelöste und fehlgeschlagene Invalidierungen sowie Datenbankabfragen und -auslastung. Verknüpfen Sie diese Signale mit jeder Schlüsselfamilie und nicht nur mit Redis als globalem Dienst.
Decken Sie in Tests mindestens den initialen Lesezugriff, die anschließende Aktualisierung, das Löschen, die Invalidierung nach dem Commit, den Cache-Ausfall und Race Conditions zwischen Leser und Schreiber ab. Prüfen Sie, dass ein Benutzer nach dem Widerruf einer Berechtigung den Zugriff verliert, dass eine Liste eine Änderung gemäß ihrem Aktualitätsvertrag widerspiegelt und dass eine fehlgeschlagene Invalidierung Warnungen oder Wiederherstellung auslöst.
Checkliste für eine bestehende PHP-Anwendung

- Listen Sie die wiederholten Lesezugriffe auf und klassifizieren Sie sie nach Risiko, Volatilität und Kosten.
- Definieren Sie die Quelle der Wahrheit und die maximal tolerierbare Veraltung für jeden Datenwert.
- Dokumentieren Sie Schlüssel, TTL, Abhängigkeiten, Consumer und Invalidierungsereignis.
- Führen Sie Invalidierungen oder Aktualisierungen erst nach dem bestätigten Commit aus.
- Schützen Sie vor gleichzeitigen Neuaufbauten und fügen Sie relevanten TTLs Jitter hinzu.
- Definieren Sie den Degradierungsmodus bei Nichtverfügbarkeit von Redis, ohne die Datenbank zu überlasten.
- Messen Sie Aktualität und Invalidierungen, nicht nur die Trefferquote.
- Überprüfen Sie regelmäßig Schlüssel ohne Eigentümer, zu hohe TTLs und nicht abgedeckte Abhängigkeiten.
Eine zuverlässige Strategie zur Cache-Invalidierung in PHP macht ihre Kompromisse sichtbar: Was kann veralten, für welchen Zeitraum, wie wird es korrigiert und was geschieht, wenn eine Komponente ausfällt. Diese Klarheit ist wertvoller, als wahllos einen Cache hinzuzufügen.



