Real-Time Backend

Caching стратегии

Instagram хранит 1 млрд фото. Без кэширования каждый просмотр профиля - запрос к PostgreSQL через Atlantic Ocean. Как 2.3 млрд пользователей получают ответ за 200ms?

  • Twitter: 300k reads/sec на таймлайны. Без кэша PostgreSQL упал бы через минуты после запуска. Redis write-through для твитов снижает DB load на 98%.
  • Binance: котировки 100+ пар криптовалют обновляются 50-100 раз/сек. Write-behind + Redis Pub/Sub доставляет цены 10M пользователям с p99 < 2ms.
  • Cloudflare: 25 млн сайтов за CDN. Cache-tag инвалидация позволяет при изменении одной страницы удалить ровно нужные ключи из 200+ edge PoP за < 150ms.
  • Discord: история сообщений 1M+ серверов. Горячие каналы в Redis с TTL 0, холодные - вытесняются LRU. Cache hit rate 94%, PostgreSQL получает только 6% запросов.

Write Through

**Write-through** - стратегия кэширования, при которой каждая запись одновременно идёт и в кэш, и в основное хранилище. Клиент получает подтверждение только после того, как данные сохранены в обеих системах.

Главное свойство write-through: кэш и БД всегда консистентны. После любого write данные в Redis и PostgreSQL идентичны - нет окна расхождения. За это платят удвоенной латентностью каждой записи.

**Когда write-through выигрывает:** данные читаются сразу после записи (профили пользователей, настройки), или допускать stale-reads нельзя (финансовые балансы, инвентарь). Facebook использует write-through для профилей: 1 млрд read/day при cache hit rate 98.5%.

  • Плюс: кэш и БД всегда синхронны, нет stale-reads
  • Плюс: при падении кэша данные не теряются - они уже в БД
  • Минус: latency каждого write увеличивается на время записи в кэш
  • Минус: write-heavy нагрузка не даёт выигрыша - кэш не буферизует

Система записывает 50k транзакций/сек. Каждая транзакция сразу читается мобильным приложением. Какую стратегию выбрать?

Write Behind

**Write-behind** (или write-back) - запись подтверждается клиенту сразу после сохранения в кэш. В БД данные попадают асинхронно - батчами или через очередь. Latency записи резко падает: клиент ждёт только Redis (~0.3ms), а не PostgreSQL (~5ms).

Twitter использует write-behind для timeline: твит попадает в Redis за 0.5ms, в Cassandra - через 50-200ms батчем. При 500k tweets/час разница в latency критична для ощущения скорости.

**Риск потери данных:** если кэш упадёт до записи в БД, несохранённые изменения пропадут. Для митигации: Redis AOF (Append-Only File) с fsync every second, интервал сброса 100-500ms, BullMQ-очередь с retry вместо in-memory буфера.

  • Плюс: write latency падает в 5-15x (только Redis, не ждать БД)
  • Плюс: батчевые записи в БД снижают I/O нагрузку на 10-50x
  • Плюс: пики трафика сглаживаются - БД получает ровный поток
  • Минус: окно потери данных при сбое кэша до flush в БД
  • Минус: усложнённая логика flush, retry, идемпотентности

Write-behind кэш сбрасывает данные в PostgreSQL каждые 200ms. Redis упал через 150ms после последнего flush. Что произошло с записями за эти 150ms?

Cache Invalidation

Cache invalidation - процесс удаления или обновления записей в кэше при изменении исходных данных. Фил Карлтон сказал: "В computer science есть только две сложные проблемы: инвалидация кэша и нейминг". Это не шутка.

Проблема: данные в кэше устаревают, но кэш об этом не знает. Пользователь A обновил профиль, пользователь B видит старую версию из кэша. Для stateless REST API это часто допустимо; для RT систем - критично.

**Cache stampede (thundering herd):** когда популярный ключ истекает, сотни запросов одновременно идут в БД. Решение - probabilistic early expiration: начинать обновление ключа до истечения TTL с вероятностью, пропорциональной близости к expiry. Netflix использует этот паттерн для предотвращения пиков на Cassandra.

  1. TTL-based: простая, но создаёт окно stale-данных равное TTL
  2. Event-driven: точная инвалидация по событиям изменения данных
  3. Cache tags: групповая инвалидация связанных ключей
  4. Versioned keys: product:v42:{id} - новая версия = новый ключ, старый истекает

Интернет-магазин: 10k одновременных пользователей смотрят страницу товара. TTL кэша истёк. Что произойдёт и как это называется?

Кэширование в RT системах

Real-time системы - чат, биржевые котировки, live-спорт - имеют особые требования к кэшированию: данные устаревают за миллисекунды, а не минуты. Классические подходы с большим TTL здесь не работают.

Ключевое отличие RT кэширования: вместо pull-модели ("дай мне данные") используется push-модель ("уведоми при изменении"). Redis Pub/Sub и Keyspace notifications позволяют кэшу сигнализировать об изменениях, не ожидая следующего запроса.

  • Short TTL (1-5 сек) для данных с высокой частотой обновления
  • Redis Pub/Sub для push-инвалидации при каждом изменении
  • Keyspace Notifications для реакции на SET/DEL без polling
  • Шардирование по entity ID: разные Redis-ноды для разных торговых пар или комнат чата

**Discord:** 1 млн+ одновременных голосовых сессий. Состояние канала кэшируется в Redis с TTL 0 (без истечения) и инвалидируется только через события. При перезапуске сервиса кэш прогревается из PostgreSQL - cold start занимает 2-3 сек на 100k каналов.

Чем больше TTL - тем лучше производительность, данные дольше живут в кэше

Оптимальный TTL балансирует hit rate и допустимое время устаревания данных. Для RT систем TTL часто 0-5 сек с event-driven инвалидацией

Большой TTL повышает hit rate, но создаёт длинное окно stale-данных. В RT контексте пользователь видящий цену акции 5-минутной давности - это критический баг, не экономия ресурсов.

Live-чат: сообщения отправляются 5000 раз/сек. TTL кэша истории - 30 секунд. Через сколько секунд пользователь может увидеть stale историю?

Итоги

  • Write-through: кэш и БД всегда синхронны, write latency выше - платят каждой записью
  • Write-behind: write latency минимальна, данные идут в БД батчем - риск потери при сбое кэша
  • Cache invalidation: TTL-based прост но создаёт stale-окно; event-driven точен но сложнее
  • RT системы предпочитают push-модель (Pub/Sub, Keyspace Notifications) вместо polling

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

Стратегии кэширования пересекаются с другими паттернами RT архитектуры:

  • Redis Pub/Sub — Механизм push-инвалидации и доставки событий при изменении кэша
  • WebSocket серверы — Получают события изменения кэша и пушат обновления подключённым клиентам
  • Шардирование данных — Кэш шардируется по тем же ключам что и БД - нарушение аффинити ломает hit rate

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

  • Какую стратегию выбрать для кэша счётчика лайков: write-through или write-behind? Что теряется при каждом варианте?
  • Cache stampede при истечении популярного ключа - какие три разных способа его предотвратить?
  • Как организовать инвалидацию кэша если один объект (пользователь) влияет на 10+ разных кэш-ключей?

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

  • db-26-caching
Caching стратегии

0

1

Войти