intermediate
Caching patterns
Применяйте cache-aside, write-through, TTL jitter, stampede protection и invalidation вокруг actual consistency needs.
Паттерны кэширования определяют, как приложение и Redis делят истину: когда читать кэш первым, когда write-through, как инвалидировать и переживать stampede. Redis — слой производительности; правила корректности остаются у source of record.
// Cache-aside (lazy loading)
async function getProduct(id) {
const cached = await redis.get(`product:${id}`)
if (cached) return JSON.parse(cached)
const row = await db.products.findById(id)
await redis.set(`product:${id}`, JSON.stringify(row), 'EX', 300 + Math.floor(Math.random() * 30))
return row
}
// Invalidate on write
await db.products.update(id, patch)
await redis.del(`product:${id}`)
| Паттерн | Идея | |---------|------| | Cache-aside | Приложение грузит кэш на miss | | Write-through | DB и кэш пишутся вместе | | Write-behind | Сначала кэш, async DB — рискованно | | TTL + jitter | Ограничение staleness; размазывание expire | | Защита от stampede | Lock, single-flight или early refresh |
Задайте допустимую staleness по сущности. Probabilistic early expiration или mutex (`SET lock NX EX`) при одновременном expire горячих ключей. Версионируйте ключи (`product:9:v3`) для rolling invalidation.
На интервью: пройдите read и write paths для одной сущности и назовите inconsistency, которую может увидеть пользователь.
Типовые ошибки: кэш как единственный источник истины; нет invalidation при update; thundering herd на популярном TTL; кэширование ошибок или пустых результатов; игнор eviction при нехватке памяти.
Компромисс — снижение latency и нагрузки vs сложность consistency: каждый паттерн переносит staleness и failure modes туда, что нужно документировать и тестировать.
Чеклист:
- Выберите cache-aside vs write-through по сущности.
- TTL с jitter на горячих ключах.
- Invalidation на каждом write path.
- Защита от stampede для топ-ключей.