System Design

Кеширование

В 2012 году Instagram после переезда на AWS зафиксировал: добавление одного слоя Redis перед PostgreSQL снизило число запросов к базе с 800 до 12 в секунду при той же нагрузке. Stack Overflow обслуживает 10 миллионов вопросов с 25 серверами - потому что 95% ответов отдаёт Memcached, а не база данных. Кеширование - это не оптимизация: без него большинство высоконагруженных систем физически не могут работать.

  • **Instagram + Redis:** добавление одного слоя Redis перед PostgreSQL снизило число запросов к БД с 800 до 12 в секунду при той же нагрузке
  • **Stack Overflow:** обслуживает 10 млн вопросов с 25 серверами - 95% ответов отдаёт L2-кеш на локальных SSD
  • **Twitter:** home timeline формируется за 2 мс благодаря pre-computed fan-out в Redis, без кеша это 300 мс на 300+ SQL-запросов
  • **Facebook Memcached:** 28 тысяч серверов Memcached хранят trillion объектов - без этого слоя MySQL лёг бы под первым же spiky traffic

Цели урока

  • Понимать, почему кеширование критично для производительности
  • Знать уровни кеширования: от браузера до БД
  • Уметь реализовать Cache-Aside pattern
  • Различать Write-Through и Write-Behind стратегии
  • Понимать способы инвалидации: TTL, explicit, event-driven
  • Знать eviction policies: LRU, LFU

Предварительные знания

  • Базовое понимание работы БД
  • Понятие latency и QPS

Зачем нужно кеширование

**Кеширование** - хранение часто запрашиваемых данных ближе к потребителю. Это самый эффективный способ уменьшить latency и снизить нагрузку на БД.

База данных отвечает за 10ms, а Redis - за 0.5ms. При 100K QPS это разница между 'работает' и 'лежит'.

  • RAM в 100-1000 раз быстрее диска
  • Локальный кеш быстрее сетевого запроса
  • 95% cache hit = база обрабатывает только 5% запросов
ИсточникLatencyОтносительно
L1 CPU Cache1 ns1x
RAM (Redis)100 ns - 1 ms~1000x
SSD (PostgreSQL)1-10 ms~10,000x
Network (другой DC)10-100 ms~100,000x

Экономия от кеша

Как кеш снижает нагрузку на БД

Без кеша: • 100K QPS → все идут в PostgreSQL • БД не выдерживает → timeout, errors С кешем (95% hit rate): • 100K QPS → 95K из Redis, 5K в PostgreSQL • Redis: 0.5ms, PostgreSQL: 10ms • Средняя latency: 0.95×0.5 + 0.05×10 = 0.975ms • БД легко справляется с 5K QPS

**Cache Hit Rate** - главная метрика кеша. 95%+ - отлично. Ниже 80% - нужно разбираться, почему так много промахов.

При cache hit rate 90% и 50K QPS, сколько запросов дойдёт до БД?

Уровни кеширования

Кеширование можно применять на каждом уровне системы. Чем ближе к пользователю - тем быстрее, но сложнее инвалидация.

УровеньLatencyShared?Инвалидация
Browser0 (local)НетCache-Control headers
CDN1-10 msДаPurge API, TTL
ApplicationμsНетIn-process
Distributed0.1-1 msДаExplicit, TTL
Database1-10 msДаAutomatic

**Не забывай про Browser Cache!** Правильные HTTP headers (Cache-Control, ETag) - бесплатная оптимизация. Статика может кешироваться на месяцы.

Какой уровень кеша shared между всеми app серверами?

Стратегия Cache-Aside

**Cache-Aside** (Lazy Loading) - самая популярная стратегия. Приложение явно управляет кешем: проверяет, читает из БД при промахе, сохраняет в кеш.

  • **Плюсы**: Простая логика, кешируем только запрашиваемые данные
  • **Минусы**: Первый запрос всегда медленный (cold cache)
  • **Когда использовать**: Read-heavy workloads, когда данные запрашиваются повторно

**Проблема Thundering Herd**: Когда TTL истекает для популярного ключа, все серверы одновременно идут в БД. Решение: добавь jitter к TTL (TTL ± random) или используй locking.

В Cache-Aside, когда данные попадают в кеш?

Стратегии записи

Как обновлять кеш при записи в БД? Два основных подхода: **Write-Through** и **Write-Behind**.

Write-Through

Write-Behind (Write-Back)

СтратегияLatencyConsistencyРиск потери
Write-ThroughВышеStrongНизкий
Write-BehindНизкаяEventualЕсть
Cache-Aside + DeleteСредняяEventualНизкий

**Рекомендация**: Write-Through для критичных данных (платежи, профиль). Write-Behind для аналитики, логов, счётчиков.

Какая стратегия лучше для записи платёжных транзакций?

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

> "There are only two hard things in Computer Science: cache invalidation and naming things." - Phil Karlton

Как держать кеш актуальным? Несколько стратегий с разными trade-offs.

TTL (Time To Live)

Explicit Invalidation

Event-Driven Invalidation

**Cache Stampede**: Когда TTL истекает для популярного ключа → все серверы идут в БД → overload. Решения: jitter (TTL ± random), probabilistic early refresh, locking.

Почему при обновлении данных лучше DELETE из кеша, а не SET?

Eviction Policies

Когда кеш переполнен (maxmemory достигнут), нужно решить, какие данные удалить. **Eviction policy** определяет эту логику.

PolicyОписаниеКогда использовать
LRULeast Recently Used - удаляем давно не используемоеУниверсальный, default
LFULeast Frequently Used - удаляем редко используемоеКогда частота важнее recency
FIFOFirst In First Out - очередьПростой, не оптимальный
RandomСлучайный выборНепредсказуемый access pattern
TTL-basedУдаляем с истекшим TTLДополнение к другим

Redis Eviction Policies

Redis vs Memcached

КритерийRedisMemcached
Структуры данныхRich (lists, sets, hashes)Только strings
PersistenceRDB, AOFНет
ThreadingSingle-threaded*Multi-threaded
Pub/SubДаНет
КластеризацияRedis ClusterРучной sharding
Когда выбратьБольшинство случаевSimple high-throughput

**Redis 6+** поддерживает I/O threading, но command execution остаётся single-threaded. Это обеспечивает атомарность операций.

Какая eviction policy лучше для кеша с unpredictable access patterns?

Ключевые выводы

  • **Cache Hit Rate** - главная метрика, цель 95%+
  • **Cache-Aside** - самая популярная стратегия для read-heavy
  • **Write-Through** для консистентности, **Write-Behind** для скорости
  • **TTL** - простой и надёжный способ инвалидации
  • **LRU** - универсальная eviction policy
  • **Redis** для большинства случаев, **Memcached** для простого high-throughput

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

Кеширование связано с другими компонентами системы

  • CDN — Edge caching - кеширование ближе к пользователю
  • Database Scaling — Кеш снижает нагрузку на БД
  • Consistency — Кеш = eventual consistency. Trade-off с freshness

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

  • При cache hit rate 80% система начала деградировать под нагрузкой. Какие причины стоит проверить первыми - рост уникальных ключей, некорректный TTL, или смещение в паттерне запросов?
  • Write-Through гарантирует консистентность, но удваивает latency записи. В каких сценариях это неприемлемо, и как тогда решается проблема консистентности без кеша?
  • LRU eviction работает плохо при скачкообразном сканировании большого датасета (cache pollution). Какие альтернативы предусмотрены в Redis для таких access pattern'ов?

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

  • ds-20-lru-cache
Кеширование

0

1

Войти