Caching-Strategien
1) Warum Caching und wo es zu tun
Ein Cache ist eine schnelle Speicherschicht, die die Latenz und Belastung teurer Ressourcen (CPU/DB/externe APIs) reduziert. Wichtige Ziele:- Geschwindigkeit (p95/p99 niedriger), Kosten (weniger egress/CPU), Robustheit (weniger Abhängigkeiten unter dem Peak).
- Glättung von Spitzen und Isolierung von „lauten Nachbarn“.
1. Client (Browser/Mobile) - HTTP-Cache, IndexedDB, lokaler Speicher.
2. Edge/CDN - POP-Knoten sind näher am Benutzer, zwischenspeichern Statik und Teil der API.
3. L7-Gateway/Reverse-Proxy - Nginx/Envoy/Varnish (Microcache, SWR).
4. Service Cache - Redis/Memcached innerhalb des Clusters.
5. In-process - in-memory (Caffeine/Guava/LRU-map).
6. Cache in der DB - materielle Darstellungen, sekundäre Indizes.
Regel: Caching so nah wie möglich an den Verbraucher, aber die Wahrheit einmal speichern.
2) Caching-Muster
2. 1 Cache-aside (“lazy loading”)
Die Anwendung liest zuerst aus dem Cache; bei einem Fehler - aus der Quelle, schreibt dann in den Cache.
Vorteile: Einfachheit, Kontrolle. Nachteile: Kaltstarts, Fenster der Fehlausrichtung.
2. 2 Read-through
Das Lesen erfolgt immer über einen Cache, der bei einem Fehlschuss selbst zur Quelle geht (Library/Proxy Layer).
Es ist bequem, TTL-Richtlinien/Seralisation zu zentralisieren.
2. 3 Write-through / Write-back (write-behind)
Schreiben durch: Schreiben in Cache und Quelle synchron → Konsistenz höher, Latenz höher.
Write-Back: Schreiben in den Cache, asynchrones Flash-Schreiben in die Quelle → schnell, aber das Risiko von Verlusten und Konflikten.
2. 4 Refresh-ahead (proactive)
Prognostiziert „TTL wird bald ablaufen“ und aktualisiert den Schlüssel im Hintergrund, um Stampede zu verhindern.
2. 5 Negative caching
Caching „keine Daten/404/leer“ auf eine kurze TTL reduziert die Belastung der Quelle.
2. 6 Micro-caching
Sehr kurze TTL (0. 5-5 s) auf L7 für „fast Dynamik“ (Listen, Haupt) - stark reduziert Schwänze.
3) HTTP-Cache: Header und Kontrolle
3. 1 Grundlegende Überschriften
`Cache-Control`: `max-age`, `s-maxage` (для shared кэшей), `public/private`, `no-store`, `stale-while-revalidate`, `stale-if-error`.
Validatoren: 'ETag' (Content Hash), 'Last-Modified'.
Anfragen mit den Bedingungen: „If-None-Match“, „If-Modified-Since“ → 304 Not Modified.
3. 2 Vary und Schlüssel
'Vary: Accept-Encoding, Authorization, Cookie, Accept-Language' - bildet verschiedene Cache-Varianten. Minimieren Sie' Vary', um die Kardinalität nicht zu „sprengen“.
3. 3 Beispiel für eine HTTP-Antwort
Cache-Control: public, max-age=60, s-maxage=300, stale-while-revalidate=60
ETag: "a1b2c3"
Vary: Accept-Encoding
4) Schlüsselentwurf und TTL
4. 1 Schlüssel
Strukturieren Sie: 'tenant: user: {id}: profile: v3' (Schemaversion einschließen).
Vermeiden Sie PII im Schlüssel.
Für Sammlungen - Schlüssel + Abfrageparameter (normalisiert und sortiert).
4. 2 TTL und Konsistenz
Eine kurze TTL reduziert die Fehlausrichtung, erhöht aber die Fehler.
Für kritische Daten: Validatoren ('ETag') und SWR (stale-while-revalidate).
Für selten wechselnde - lange TTL + „Bomben“ Behinderung.
4. 3 Versionierung/Basting
Bei inkompatiblen Änderungen ändern Sie das Präfix/die Version des Schlüssels ('v2 → v3').
Für statische Ressourcen - content hash im Dateinamen.
5) Behinderung: Strategien und Praktiken
5. 1 Direkte Löschung
„DEL-Schlüssel “/„ PURGE“ auf Proxy. Die Gefahr: ein Wettlauf zwischen Löschung und mehrfachen Lesern.
5. 2 Tags/Surrogate keys
Ordnen Sie das Dokument einem Tag-Set (Kategorie/Autor) zu. Behinderung - nach dem Tag.
В Varnish/Edge — `Surrogate-Key: article:42 tag:author:7` + `BAN tag:author:7`.
5. 3 Event-getriebene Behinderung
Pub/Sub (Kafka/NATS): Wenn sich die Quelle ändert, veröffentlichen wir das Ereignis „invalidate“.
Cache-Consumers hören zu und löschen/aktualisieren die Schlüssel.
5. 4 Zweiphasig
Zuerst markieren wir den Schlüssel als veraltet (soft TTL), warten den Stale, im Hintergrund erneuern wir ihn und ersetzen ihn atomar.
6) Kampf gegen stampede/dogpile und heiße Schlüssel
6. 1 Request coalescing (singleflight)
Ein Produzent aktualisiert den Schlüssel, der Rest wartet auf das Ergebnis (mutex/label „update“).
6. 2 Jitter к TTL
Fügen Sie der TTL Zufälligkeit (± 10-20%) hinzu, um synchrones Schwinden zu vermeiden.
6. 3 Soft-TTL + hard-TTL
Vor Soft-TTL bedienen wir aus dem Cache, parallel zu den Refresh-Triggern; von hard-TTL - wir halten das für einen Ausrutscher.
6. 4 Hot Keys
Lokale Caches über gemeinsamen Caches (zwei-Tier).
Replikation des Hotkeys in mehrere Shards und Zufallsauswahl (nur für Read-Only).
Rate Limit für die Aktualisierung eines bestimmten Schlüssels.
6. 5 Beispiel Redis + Lua (Singleflight-Skizze)
lua
-- SETNX lock with TTL to avoid deadlocks local ok = redis. call("SET", KEYS[1], "1", "NX", "EX", ARGV[1])
if ok then return "LOCKED"
else return "WAIT"
end
7) Push-Richtlinien und Cache-Empfang
7. 1 Eviction
LRU: einfach und gut für die Lokalität.
LFU: besser mit „langlebigen“ Hot Keys.
ARC/TinyLFU: recency/frequency balance.
7. 2 Zulassung (Einlass)
Lassen Sie keine riesigen seltenen Objekte (TinyLFU/Bloom-Filter) hinein.
Kompression großer Werte (LZ4/Zstd) an der Grenze „Größe/Latenz“.
8) Sharding und Topologien
8. 1 Consistent hashing
Verteilt Schlüssel stabil auf Knoten, reduziert Bewegungen beim Wachstum/Komprimieren des Clusters.
8. 2 Redis/Memcached Topologien
Redis Cluster (Slots/Shards), Sentinel (Failover), nur lesen Replikation.
Memcached ist ein Sharding-Client-Side (Ketama-Hashing) ohne Replikation auf Serverebene.
8. 3 Lokal + verteilt
Kaskade: in-proc (micro-TTL/LRU) → Redis (TTL länger) → Quelle.
Seien Sie vorsichtig mit TTL-Doppelpunkten und Cache-Validierern.
9) Edge, CDN und L7-Cache
9. 1 Micro-cache на Nginx
nginx proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api:100m inactive=10m;
map $request_method $skip_cache { default 0; POST 1; PUT 1; DELETE 1; }
server {
location /api/list {
if ($skip_cache) { add_header Cache-Control "no-store"; }
proxy_cache api;
proxy_cache_valid 200 2s; # micro-cache proxy_cache_use_stale error timeout updating;
proxy_cache_background_update on; # SWR add_header X-Cache $upstream_cache_status;
proxy_pass http://upstream;
}
}
9. 2 Envoy (SWR und Bedingungen)
yaml http_filters:
- name: envoy. filters. http. cache typed_config:
"@type": type. googleapis. com/envoy. extensions. filters. http. cache. v3. CacheConfig typed_config:
"@type": type. googleapis. com/envoy. extensions. http. cache. file_system_http_cache. v3. FileSystemHttpCacheConfig cache_path: "/var/cache/envoy"
9. 3 Varnish (Surrogate keys)
Verwenden Sie' Surrogate-Key 'und' ban 'durch Tags für Paket-Behinderung.
10) Cache und Datenkonsistenz
10. 1 Read-your-writes
Stellen Sie für benutzerdefinierte Profile/Warenkörbe entweder eine kurze TTL oder eine Aufzeichnung durch den Cache (write-through) oder eine Clientkennzeichnung (bypass für N Sekunden nach der Aufzeichnung) bereit.
10. 2 Eventual vs Strong
Für Empfehlung/Analyse - eventual + long TTL.
Für Geld/Auftragsstatus - kurze TTL, Validierung, manchmal ohne Cache auf kritischen Pfaden.
10. 3 Invarianten
Keine Felder, die die Sicherheit/ACL beeinflussen, ohne strenge TTL und erneute Überprüfung zwischenspeichern.
11) Beobachtbarkeit, SLO und Management
11. 1 Metriken
hit_ratio (общий и per-route), byte_hit_ratio, miss_rate.
stampede_prevented_total, refresh_ahead_total, ban/purge_total.
Latenz: p50/p95/p99 aus dem Cache vs von der Quelle.
hot_keys_topN und ihre QPS/Bytes.
11. 2 Protokolle und Traces
Loggen Sie' X-Cache: HIT/MISS/STALE/UPDATING'.
Markieren Sie in Traces die Quelle der Antwort ('cache = true', 'tier = edge' service' local').
11. 3 SLO-Ansatz
Beispiel: "für API/Katalog p99 ≤ 250 ms, Cache-Hit ≥ 85%, Stampede ≤ 0. 1% der Anfragen".
11. 4 Runbooks
„Fehler wachsen“ → Überprüfen Sie die TTL, Aufwärmen/Invalidität, Hot-Keys, Cache-Größe und Akzeptanzrichtlinien.
12) Sicherheit und Multi-Tenant
Betten Sie tenant-id in die Schlüssel ein (und in 'Vary' unter HTTP).
Geben Sie keine privaten Antworten als' öffentlich 'ein.
Verschlüsseln Sie den Cache mit sensiblen Daten oder speichern Sie nur Nicht-PII/ID.
13) Typische Rezepte
13. 1 Katalog/Band (fast Dynamik)
Edge-Microcache 1-3 mit + SWR, innen Redis für 15-60 s, Behinderung durch Update-Events.
13. 2 Benutzerprofil
Cache-aside mit TTL 30-120 s, bypass 5-10 s nach Profilaktualisierung (Cookie/Header) oder write-through.
13. 3 Valuto-Kurse/Handbücher
Lange TTL (Minuten-Stunden) + gezielte Behinderung bei der Veröffentlichung neuer Daten; 'ETag' für bedingte GETs.
13. 4 Suchausgabe
Edge-Microcache 1-2 s, innen refresh-ahead und coalescing, Normalisierung der Query-Parameter im Schlüssel.
14) Anti-Muster
Cash ohne Behinderung: Hoffnung nur auf TTL → lange Fenster der Irrelevanz.
Giant 'Vary': „Explosion“ von Optionen → niedrige Trefferquote.
Ein einziger Cache für prod/experiments → Kontamination.
Kein Schutz vor Stampede → Peaks an der Quelle, wenn die TTL abläuft.
Geld/Rechte/ACL-Cache ohne strenge Garantien.
Kompression „nur in einer Reihe“ - zusätzliche CPUs, Verschlechterung p99 auf kleinen Objekten.
15) Checkliste Umsetzung
- Definieren Sie die Cache-Ebenen und ihre Ziele (edge/service/local).
- Schlüssel entwerfen (Versionierung, Tenant, Normalisierung der Parameter).
- Wählen Sie ein Muster aus (cache-aside/read-through/refresh-ahead).
- TTL/soft-TTL/jitter einrichten, SWR einschalten.
- Implementiere Coalescing/Singleflight, Schutz vor Stampede.
- Behinderung organisieren (Events, Tags, Purge/Ban).
- Geben Sie Hit-Ratio/Latenzmetriken und 'X-Cache' Dashboards ein.
- Führen Sie Lasttests mit Hot Keys durch.
- Schreiben Sie SLOs und Runbooks.
- Überprüfen Sie die Sicherheit/tenant-Isolierung und 'Vary'.
16) FAQ
F: Was soll ich wählen - Cache-Aside oder Read-Through?
A: Für einfache Dienste - cache-aside. Wir brauchen Zentralisierung und eine einheitliche Politik - read-through.
F: Wie kann man die optimale TTL verstehen?
A: Verlassen Sie sich auf die zulässige Veralterung, die Häufigkeit der Aktualisierungen und die Zieltrefferquote; fügen Sie jitter hinzu und beobachten Sie p95/p99/Kosten.
F: Wann ist Write-Back angemessen?
A: Für hochbelastete Ströme, bei denen eine eventuelle Konsistenz akzeptabel ist und es eine zuverlässige Warteschlange/ein Protokoll zum „Beenden“ gibt.
F: Können autorisierte Antworten zwischengespeichert werden?
A: Ja, aber markieren Sie' privat 'und/oder schließen Sie tenant/user in den/' Vary' Schlüssel ein. Für truly-private - Client-Cache.
F: Wie kann ich meinen Cache aufwärmen?
A: Listen von beliebten Schlüsseln, Hintergrund-Wormer, Replay von Protokollen, Aufwärmen vor Release/Peak (Black Friday, etc.).
17) Ergebnisse
Effektives Caching ist Schlüsseldesign + vernünftige TTL + ein geschickt ausgewähltes Muster, verstärkt durch Behinderung durch Events, SWR/refresh-ahead und Schutz vor Stampede. Ordnen Sie den Cache nach Ebenen (Client/Edge/Service), fügen Sie Beobachtbarkeit und SLO hinzu - und erhalten Sie stabile Latenzschwänze, vorhersehbare Kosten und Widerstandsfähigkeit gegen Spitzenlasten.