Real-Time Backend

In-Memory структуры

Steam показывает живой рейтинг миллионов игроков, Twitter определяет trending topics за секунды, Google не краулит одни и те же URL дважды из миллиардов - всё это делается без запросов к диску.

  • **Steam/Clash of Clans**: leaderboards на Redis Sorted Set - ZADD обновляет позицию игрока атомарно после каждого матча, ZRANGE возвращает топ-100 за 0.3 мс
  • **Twitter trending**: HyperLogLog считает уникальных пользователей, упомянувших тег, используя фиксированные 12 KB памяти - независимо от числа твитов
  • **Google/Bing web crawler**: Bloom Filter хранит миллиарды посещённых URL в ~1.2 GB вместо 40+ GB - false positive 1% допустим, зато нет повторного краулинга
  • **GitHub/Stripe rate limiting**: Redis INCR + EXPIRE считает запросы за скользящее окно атомарно - 10 000 RPS без единого обращения к PostgreSQL

Inmemory State

In-memory хранилища держат данные в RAM, а не на диске. Чтение занимает микросекунды вместо миллисекунд - это разница в 100-1000 раз. Для real-time систем, где задержка > 10 мс уже заметна пользователю, это не оптимизация, а необходимость.

Базовая математика: SSD-чтение ~ 100 мкс, RAM-чтение ~ 0.1 мкс. Leaderboard из 1 млн записей: PostgreSQL с индексом - 5-20 мс, Redis Sorted Set - 0.3-1 мс. При 10 000 RPS разница критична.

  • **Session state** - миллиарды активных сессий хранятся в Redis, не в PostgreSQL
  • **Rate limiting** - счётчики запросов за секунду невозможно считать с диска в реальном времени
  • **Leaderboards** - живые рейтинги с мгновенным обновлением ранга после каждого действия
  • **Pub/Sub** - маршрутизация событий между сервисами без записи на диск

Почему in-memory хранилища используют для rate limiting, а не PostgreSQL с индексом?

Redis Structures

Redis - не просто key-value кеш. Каждая структура данных решает конкретный класс задач: Sorted Sets для рейтингов, Hashes для объектов, HyperLogLog для подсчёта уникальных элементов, Bloom Filter для проверки принадлежности. Правильный выбор структуры - разница между 1 мс и 100 мс.

HyperLogLog использует 12 KB RAM независимо от числа уникальных элементов. Подсчёт 1 млрд уникальных IP-адресов в Set потребовал бы ~8 GB, HyperLogLog - те же 12 KB с погрешностью 0.81%. Twitter использует этот подход для trending topics.

  1. **String/INCR** - счётчики, rate limiting, distributed locks (SETNX)
  2. **Hash** - хранение объектов (session, user profile) с доступом к отдельным полям
  3. **Sorted Set** - leaderboards, priority queues, временные ряды с score=timestamp
  4. **HyperLogLog** - подсчёт уникальных элементов с фиксированной памятью 12 KB
  5. **Bloom Filter** - быстрая проверка 'посещали ли URL' без хранения всех значений

Игра хранит очки 10 млн игроков и показывает топ-100 с мгновенным обновлением после каждого матча. Какая структура Redis подходит?

Memcached

Memcached - это намеренно простое key-value хранилище: строки, TTL, LRU-вытеснение. Никаких persistence, репликации или сложных структур. Facebook в пике нагрузки держал > 1000 серверов Memcached перед MySQL - это крупнейший в истории Memcached кластер.

Memcached быстрее Redis на простом GET/SET благодаря multi-threading: все ядра CPU обрабатывают запросы параллельно. Redis 6+ добавил IO threads, но основной цикл команд остался однопоточным. При > 500 000 RPS на простом кешировании Memcached может выиграть.

  • **Выбирать Memcached** когда нужно только кешировать простые значения при очень высоком RPS (> 500k)
  • **Выбирать Redis** для всего остального: persistence, pub/sub, сложные структуры, rate limiting
  • **Facebook** использует оба: Memcached для L1-кеша страниц, Redis для session и realtime-фич
  • **Горизонтальное масштабирование Memcached** делается на стороне клиента через consistent hashing

Сервис кеширует HTML-страницы (10 KB каждая) для 50 млн пользователей. Persistence и pub/sub не нужны. Что предпочтительнее?

Custom Inmemory

Иногда Redis и Memcached избыточны или недостаточны. Кастомные in-memory структуры внутри процесса - HashMap в Node.js, concurrent Map в Go, ConcurrentHashMap в Java - дают задержку < 0.01 мс без сетевого round-trip. Цена: данные не переживают рестарт, не шарятся между процессами.

LRU Cache - классический пример кастомной in-memory структуры. Node.js: пакет lru-cache. Go: hashicorp/golang-lru. Используется внутри самого Redis (для key eviction), в браузерах (DNS cache), в CDN edge-узлах для hot assets.

  • **Process-local Map** - нулевая задержка, но данные не шарятся между инстансами
  • **LRU Cache** - ограничивает размер, вытесняет редко используемые ключи автоматически
  • **Граница применимости**: single-instance или stateless (edge) - local; multi-instance с shared state - Redis
  • **Hybrid**: local LRU как L1-кеш, Redis как L2 - паттерн используют Cloudflare Workers и Fastly

Redis - это просто быстрый кеш поверх базы данных, его всегда можно заменить базой с индексом

Redis - это специализированное хранилище структур данных. Sorted Set, HyperLogLog, Bloom Filter не имеют эквивалентов в реляционных базах с той же производительностью

ZADD + ZRANGE для leaderboard из 10 млн записей работает за O(log N) ~0.3 мс. Эквивалент в PostgreSQL - ORDER BY со scan или обновление materialized view - 5-50 мс. При real-time требованиях это принципиальная разница, а не детали реализации.

Микросервис деплоится в 10 инстансов. В каком случае кастомный in-memory Map заменит Redis?

Итоги

  • **Структура = задача**: Sorted Set для рейтингов, Hash для объектов, HyperLogLog для уникальных подсчётов, Bloom Filter для dedup - каждая решает конкретный класс задач за O(1)-O(log N)
  • **Memcached быстрее Redis на простом GET/SET** за счёт multi-threading, но проигрывает по функциональности - Facebook использует оба в связке
  • **Local Map** быстрее Redis (нет сетевого round-trip), но не шарится между инстансами - подходит только для статичных или инстанс-специфичных данных

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

In-memory структуры пересекаются с несколькими архитектурными слоями:

  • Pub/Sub и очереди — Redis также используется как pub/sub брокер - тот же инстанс, другие структуры (List, Stream)
  • Горизонтальное масштабирование — При масштабировании сервисов local state становится проблемой - Redis решает shared state между инстансами
  • CAP теорема — Redis Cluster выбирает AP: при split-brain принимает запись, возможна потеря данных - важно для критичных данных
  • Кеширование и инвалидация — TTL и LRU - встроенные механизмы инвалидации кеша в Redis и Memcached

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

  • Если нужно отслеживать топ-1000 активных товаров в интернет-магазине по числу просмотров в реальном времени - какую структуру Redis использовать и почему именно её?
  • Когда Bloom Filter лучше HashSet, а когда HashSet лучше Bloom Filter - какой trade-off определяет выбор?
  • Сервис масштабируется с 1 до 20 инстансов. Какие данные можно оставить в local Map, а какие обязательно перенести в Redis?

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

  • db-19-redis
In-Memory структуры

0

1

Войти