Стратегия кеширования
Где кешировать, на сколько, как инвалидировать — кеш который не лжёт.
Спроектируй кеширование.
Закон Phil Karlton: "Есть только две сложные вещи в Computer Science: cache invalidation and naming things."
Слои кеша
Browser → CDN → Reverse proxy → App → Memory cache → DB
Чем ближе к юзеру — тем быстрее. Чем дальше — тем актуальнее.
1. Browser cache
HTTP headers:
Cache-Control: public, max-age=31536000, immutable # 1 год для assets
Cache-Control: public, max-age=0, must-revalidate # для HTML (всегда проверять)
Cache-Control: private, no-store # для sensitive
ETag для conditional requests:
GET /api/users → 200 + ETag: "abc123"
GET /api/users + If-None-Match: "abc123" → 304 (без body)
2. CDN cache
Cloudflare / Vercel Edge / AWS CloudFront:
- Static assets — cache forever (hash в имени файла)
- HTML — cache минутами, поскольку часто меняется
- API responses — selectively (с осторожностью)
Cache key
- URL + query
- Поскольку cookie / auth — обычно NO cache (private)
- Локализация — отдельные ключи
3. App-level cache (in-memory)
Для:
- Configurations
- Hot computations
- Static data
import LRU from 'lru-cache';
const cache = new LRU({ max: 1000, ttl: 60_000 });
function getUser(id) {
if (cache.has(id)) return cache.get(id);
const user = fetchFromDB(id);
cache.set(id, user);
return user;
}
Лимиты
- Размер (max items)
- TTL (time to live)
- Memory (если объекты большие)
4. Distributed cache (Redis)
Для:
- Cross-instance (несколько серверов)
- Сессии
- Rate limiting counters
- Pre-computed aggregations
const cached = await redis.get(`user:${id}`);
if (cached) return JSON.parse(cached);
const user = await db.findUser(id);
await redis.setex(`user:${id}`, 300, JSON.stringify(user));
return user;
5. DB query cache
Многие БД имеют свой:
- PostgreSQL: pg_stat_statements (просто статистика)
- MySQL: query cache (deprecated в 8.0)
- Лучше: materialised views для тяжёлых aggregates
Стратегии invalidation
A. TTL (Time-To-Live)
Самое простое. Кеш сам истечёт.
Минусы:
- До истечения — старые данные
- Если данные часто меняются — много инвалидаций
B. Event-based
При update в DB → invalidate соответствующие ключи.
async function updateUser(id, data) {
await db.update(id, data);
await redis.del(`user:${id}`);
await redis.del(`users:list`);
}
Минусы:
- Можно забыть какой ключ инвалидировать
- Сложно при много сервисах
C. Versioning
Ключ включает версию:
users:v123:list
При обновлении — увеличиваем версию. Старые ключи остаются (пока не expire), но не используются.
D. Cache-aside vs write-through
- Cache-aside (lazy): app проверяет cache, грузит из DB на miss, заполняет cache. Простой.
- Write-through: app пишет в cache + DB одновременно. Cache всегда актуален. Сложнее.
Что НЕ кешировать
- Per-user данные в shared cache (privacy)
- Случайные / некэшируемые ответы
- Очень редкие запросы (не stoit memory)
- То что должно быть real-time (stock prices)
Тонкости
Cache stampede
Когда кеш истёк → 1000 запросов одновременно лезут в DB.
Защита:
- Lock первого, остальные ждут
- Pre-warming перед истечением
- Stale-while-revalidate
Cache pollution
Bot scraper заполнил cache редкими ключами → нужные вытеснены.
Защита:
- LRU с приоритетами
- Bot detection
- Per-key TTL
Метрики
- Hit rate (>80% хорошо, <50% плохо)
- Miss rate
- Eviction rate
- Memory usage
- Latency hit vs miss
Анти-паттерны
- ❌ Кешировать всё подряд (бесполезно тратит memory)
- ❌ TTL бесконечный без invalidation
- ❌ Per-user данные в global cache → leak
- ❌ Не учитывать stale data (юзер видит старое)
- ❌ Cache в frontend без указания версии
Чек-лист
- Cache headers на статике
- CDN перед app server
- In-memory cache для hot reads
- Redis для cross-instance
- Invalidation strategy выбрана
- Hit rate отслеживается
- Stampede protection
Аудит производительности (Core Web Vitals)
Глубокая проверка LCP, INP, CLS с привязкой к коду и приоритизированным планом исправлений.
Мастер-аудит сайта: 6 измерений за один проход
Orchestrator-аудит по 6 направлениям: UX, accessibility, performance, SEO, brand consistency, security. Quick scan + deep dive + приоритизированный план + композитная оценка + roadmap.
Performance budget по типам страниц
Бюджеты JS/CSS/images для разных типов страниц, целевые Web Vitals, enforcement в CI с конкретными порогами.