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.
- TTL-based: простая, но создаёт окно stale-данных равное TTL
- Event-driven: точная инвалидация по событиям изменения данных
- Cache tags: групповая инвалидация связанных ключей
- 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+ разных кэш-ключей?