Cachowanie przechowuje wyniki kosztownych operacji (zapytania do bazy, obliczenia, zewnętrzne API) tak aby powtórzone żądania mogły być obsłużone szybko bez powtarzania pracy. To jedna z największego wpływu optymalizacji wydajności dla API.
Cachowanie w pamięci (najszybsze, pojedyncza instancja)
const cache = new Map();
async function getUser(id) {
if (cache.has(id)) return cache.get(id); // cache HIT — instant
const user = await db.findUser(id); // cache MISS — do the work
cache.set(id, user);
return user;
}
Proste i niezwykle szybkie, ale cache jest dla każdego procesu (nie współdzielony między instancjami) i ginie przy restarcie. Dobre dla małych, gorących danych — ale wymaga eksmisji (użyj LRU cache jak lru-cache) aby ograniczyć pamięć.
Rozproszone cachowanie z Redisem (współdzielone, skalowalne)
import { createClient } from "redis";
const redis = createClient();
async function getUser(id) {
const cached = await redis.get(`user:${id}`);
if (cached) return JSON.parse(cached); // hit
const user = await db.findUser(id);
await redis.setEx(`user:${id}`, 3600, JSON.stringify(user)); // cache for 1h (TTL)
return user;
}
Redis to standard dla cachowania na wielu instancjach serwera — współdzielony, szybki magazyn, który przetrwa restarty i skaluje się z aplikacją. TTL (time-to-live) automatycznie wygasa wpisy aby dane nie zostały nieaktualne na zawsze.
Cachowanie HTTP (pozwól klientom/CDN-om cachować)
res.set("Cache-Control", "public, max-age=3600"); // browsers/CDN cache for 1h
res.set("ETag", hash); // conditional requests (304 Not Modified)
Nagłówki HTTP cache przesuwają cachowanie do przeglądarek i CDN-ów całkowicie — najszybszy cache to żądanie które nigdy nie dociera do twojego serwera.
Trudna część: unieważnianie cache
// when data changes, the cache must be updated or cleared, or users see stale data
async function updateUser(id, data) {
await db.updateUser(id, data);
await redis.del(`user:${id}`); // INVALIDATE — next read repopulates with fresh data
}
Największym wyzwaniem cachowania jest unieważnianie — zapewnienie że aktualizacje czyszczą/odświeżają dotknięte wpisy cache aby użytkownicy nie widzieli starych danych. Strategie: TTL (zaakceptuj krótkie nieaktualności), write-through (aktualizuj cache przy zapisie), lub jawne usunięcie przy mutacji.
Co cachować (i czego nie)
✓ Read-heavy, infrequently-changing data (config, product catalogs, user profiles)
✓ Expensive computations, external API responses
✗ Highly dynamic/personalized or rapidly-changing data (or use very short TTLs)
✗ Sensitive data without care for who can read the cache
Dlaczego to ważne
Cachowanie dramatycznie zmniejsza latencję i obciążenie bazy/zewnętrzne, często różnica między wolnym API a szybkim, skalowalnym.
Znajomość warstw — in-memory (najszybsze, dla instancji, ograniczone przez LRU), Redis (współdzielone między instancjami), i HTTP/CDN caching (przesunięcie całkowicie) — pozwala wybrać właściwą strategię.
I zrozumienie centralnego problemu cachowania, unieważnienia (przez TTL lub jawne czyszczenie przy zapisach), to co uniemożliwia klasyczną porażkę trade-off'u serwowania użytkownikom starych danych.
Efektywne cachowanie to jedno z największego wpływu narzędzi dla wydajności i skalowalności API.
