Базы данных

Стратегии кеширования

Stack Overflow обслуживает 10 миллионов страниц в день командой из нескольких инженеров на 9 серверах. Секрет: Redis cache с hit rate 95%+. Когда популярный вопрос читают тысячи раз - ответ уже в Redis, SQL не нужен. Без кеша им потребовалось бы 100+ серверов.

  • **Stack Overflow**: Redis cache для 95% трафика - 9 web серверов vs 100+ без кеша
  • **Facebook**: write-behind для лайков (Redis INCR) + async flush в MySQL - 4.7 млрд лайков в день
  • **Netflix**: EVCache (Memcached) с probabilistic refresh - нулевые stampedes при 230+ млн подписчиков

Cache-Aside (Lazy Loading)

Cache-aside - наиболее популярная стратегия. Приложение само управляет кешем: при промахе (cache miss) читает из БД и кладёт в кеш. Преимущества: кеш содержит только реально запрошенные данные, не все. Недостатки: при старте холодный кеш = все запросы идут в БД.

Cache hit rate - ключевая метрика. 95%+ hit rate = хорошо. При 80% rate каждый 5-й запрос идёт в БД. Для Stack Overflow это всё равно миллионы запросов в БД в день. Мониторинг: Redis INFO stats -> keyspace_hits / (keyspace_hits + keyspace_misses).

После обновления данных пользователя в БД приложение делает redis.delete(cache_key). Что произойдёт при следующем чтении?

Write-Through и Write-Behind

Write-through: запись идёт одновременно в кеш и БД. Кеш всегда актуален. Минус: каждая запись = двойной overhead. Write-behind (write-back): запись только в кеш, асинхронная запись в БД. Быстро, но риск потери данных при падении кеша.

СтратегияRead latencyWrite latencyРиск потери данных
Cache-asideВысокий при missНормальныйНет (БД - источник истины)
Write-throughНизкий (всегда актуально)ДвойнойНет
Write-behindНизкийОчень низкийДа (данные в кеше не в БД)

Facebook Thundering Herd: одновременное падение кеша для популярных данных -> все запросы идут в БД. Решение: lease mechanism в Memcached. Первый запрос получает lease и идёт в БД. Остальные ждут. Кеш обновлён - все получают данные из кеша.

Facebook обновляет количество лайков поста. Миллион пользователей видят этот пост. Какая стратегия кеширования оптимальна для счётчика лайков?

TTL и инвалидация кеша

TTL (Time-To-Live) - автоматическое устаревание кеша. Проблема: слишком короткий TTL = частые cache miss = нагрузка на БД. Слишком длинный TTL = stale data. "Двух жёстких проблем в CS: именование и инвалидация кеша" (Phil Karlton).

Cache stampede (dog-pile effect): TTL истёк для популярного ключа -> множество запросов одновременно идут в БД -> БД перегружена. Решение: probabilistic early reexpiration (PER): случайно обновлять кеш чуть раньше истечения TTL с вероятностью пропорциональной оставшемуся времени.

Какая проблема возникает если тысячи пользователей одновременно запрашивают один популярный ключ после истечения его TTL?

Cache Stampede и решения

Cache stampede - параллельные запросы к одному холодному ключу. Решения: mutex lock (только один запрос идёт в БД, остальные ждут), probabilistic early expiration (обновлять чуть раньше истечения), background refresh (async обновление до истечения), jitter TTL (добавить случайность).

Netflix использует EVCache (Memcached-based) с probabilistic refresh для кеша контента. Страница с топ-100 фильмами обслуживается из кеша, обновляется в фоне до истечения TTL. Zero cache stampede на 230+ миллионов подписчиков.

Jitter TTL помогает избежать cache stampede. Как именно?

Distributed Cache

Distributed cache (Redis Cluster, Memcached) распределяет кеш по нескольким узлам. Horizontal scaling: больше памяти, больше throughput. Consistent hashing: при добавлении узла перемещается минимум ключей. Проблема: network latency (~1ms) vs local cache (~microseconds).

Cache coherence в distributed системе: несколько app серверов с L1 кешем могут хранить разные версии одних данных. Решения: short TTL для L1 (1-60 секунд), publish-invalidation через Redis Pub/Sub при обновлении.

Кеш всегда ускоряет приложение

Кеш ускоряет read-heavy workload. Write-heavy приложения (много обновлений = частые инвалидации) получают overhead без выигрыша. Кеш также может скрывать проблемы с индексами.

Cache hit rate <80% при write-heavy = постоянные misses и двойная работа. Сначала оптимизировать запросы и индексы. Кеш - слой поверх хорошо работающей БД.

L1 кеш (in-process) имеет данные пользователя. Другой app сервер обновил этого пользователя. Проблема?

Итоги

  • **Cache-aside**: самый популярный паттерн; invalidate on write, fill on miss; мониторить hit rate
  • **Cache stampede**: при холодном кеше - thundering herd; решения: mutex, jitter TTL, probabilistic early expiration
  • **Distributed cache**: L1 (in-process, microseconds) + L2 (Redis, milliseconds) + DB; stale L1 = publish-invalidation

Связанные темы

Кеширование взаимодействует с несколькими паттернами:

  • Redis — Redis - стандарт для distributed cache; все структуры данных Redis используются для кеширования
  • Connection Pooling — Кеш снижает нагрузку на соединения с БД
  • Мониторинг — Cache hit rate - ключевая метрика производительности системы

Вопросы для размышления

  • При каком hit rate кеш начинает реально помогать? Как измерить ROI кеширования?
  • Как инвалидировать кеш для агрегатных данных (например, общая сумма заказов пользователя)?
  • Cache vs Materialized View: когда что использовать для предвычисленных данных?

Связанные уроки

  • arch-09-cache
Стратегии кеширования

0

1

Войти