Технологии и Инфраструктура → Кеш-уровни и хранение данных
Кеш-уровни и хранение данных
1) Зачем нужен многослойный кеш
Кеш — это короткий путь к ответу без похода в «дорогие» подсистемы (БД, внешние API, сети). Многослойность распределяет нагрузку: браузер → CDN/edge → прикладной слой → распределенный кеш → БД/хранилище. Цели: снизить P95/P99, разгрузить origin, крепче выдерживать пики и удешевить байт.
2) Карта уровней кеша
1. Браузер: `Cache-Control`, `ETag`, `Last-Modified`, `stale-while-revalidate`.
2. CDN/Edge: TTL/ключ, Vary, Signed URLs, image-resize; tiered/shield.
3. API Gateway/Service Mesh: краткоживущий ответный кеш по безопасным GET.
4. Приложение (in-process): LRU/LFU, near-cache для «горячих» ключей, миллисекунды.
5. Распределенный кеш (Redis/Memcached): основной слой для динамики.
6. Кэши БД: Pg/Innodb буферы, PgBouncer multiplexing, materialized views.
7. Дисковые/объектные сторы: precomputed snapshots, blob-кеш (например, S3+CDN).
Принцип: «чем ближе к пользователю — тем короче TTL и меньше персонализации; чем ближе к данным — тем богаче политика консистентности».
3) Паттерны кеширования
Cache-Aside (Lazy): читаем → при MISS грузим из источника → кладем в кеш. Прост, дает контроль TTL.
Read-Through: приложение читает через кеш, который сам тянет из источника. Удобно централизовать политику.
Write-Through: запись идет в кеш и в источник сразу. Консистентнее, но дороже по записи.
Write-Back (Write-Behind): пишем в кеш, источник обновляется асинхронно (очередь). Высокая скорость, требуются гарантии доставки и идемпотентность.
Refresh-Ahead: у «топовых» ключей обновляем значение до истечения TTL.
Где что: карточки игр/каталоги — cache-aside/read-through; счетчики/лидерборды — write-back + CRDT/агрегации; справочники валют/лимитов — read-through с контролируемым TTL.
4) Ключи, сегментация и нейминг
Шаблон: `domain:entity:{id}:v{schema}|region={R}|currency={C}|lang={L}`.
Включайте в ключ только то, что реально меняет ответ (регион, валюта, язык, версия схемы).
Версионирование схем: при несовместимых изменениях — повышайте `vN` в ключе, избегая массовых purge.
Namespacing по продуктам/тенантам: `tenant:{t}:…` — критично для multi-tenant.
Bloom-фильтр на «существование ключа» может снизить походы в источник.
5) TTL, свежесть и инвалидация
TTL-матрица:- статика (хешированные файлы): 30–365 дней + `immutable`;
- каталоги/баннеры: 5–60 минут + `stale-while-revalidate`;
- лидерборды/котировки: 2–15 секунд;
- справочники (валюты/лимиты): 1–10 минут.
- Инвалидация событиями: публикуем `product.updated` → инвалидация точечного ключа/префикса.
- Tag-based purge: групповые очистки по тегам (релиз промо/каталога).
- Soft-Expiry: по истечении TTL отдаем устаревшее как `stale`, параллельно обновляем (SWR/SIE).
- Versioned Keys > массовый purge: дешевле и безопаснее.
6) Stampede, «горячие» ключи и конкуренция
Dogpile/Stampede защита:- Single-flight (request coalescing): один лидер обновляет ключ, остальные ждут.
- TTL джиттер: размываем истечения, избегая одномоментного обвала.
- SWR локально: просроченное значение отдаем пользователю, в фоне обновляем.
- репликация «горячего» ключа в несколько слотов `key#1..N`, распределенный чтением;
- near-cache в памяти процесса;
- prewarm/refresh-ahead перед пиками (турниры/матчи).
- Лимиты на конкарренси обновления для тяжелых ключей.
7) Консистентность и кросс-слои
Write-invalidate: при записи в источник — синхронно инвалидируйте соответствующие ключи (pub/sub).
Read-repair: при расхождениях обновляйте кеш корректным значением.
Eventual vs Strong: критичные денежные операции читаем напрямую/с коротким TTL; UI-витрины и статистика — eventual.
CRDT/агрегаторы: для распределенных счетчиков/рейтингов — структуры «merge-safe» (G-Counter, Top-K на потоках).
Каскадная инвалидация: обновление «игры» инвалидирует карточку + список + пользовательский кэш рекомендаций.
8) Сериализация, сжатие и формат
Форматы: протобуф/MessagePack быстрее JSON; для CDN/браузера — JSON с Brotli.
Компрессия в Redis: выгодна для объектов >1–2 КБ, но следите за CPU.
Partial responses / поля по требованию: меньше байт → меньше TTFB и RAM.
9) Политики вытеснения и размер
LRU (по умолчанию) — безопасно; LFU — лучше для «популярного» контента.
Размер ключей/значений: держите под контроль (метрики `avg value size`, `max`).
Квоты по namespace/tenant, чтобы один продукт не «съел» весь кеш.
10) Безопасность и PII/PCI
Личные/финансовые данные — не кешировать на CDN/edge и в общих слоях; используйте токены/проекции.
Шифрование чувствительных значений в Redis через client-side crypto (с осторожностью к потерям TTL-контроля).
Строгие ACL и изоляция сетей; фиксированные NAT/IP для egress к провайдерам.
11) Наблюдаемость и SLO кеша
Метрики:- Hit Ratio (по слоям и префиксам), Origin Offload.
- TTFB/P95/P99 до/после кеша, Latency Redis.
- Evictions, OOM, keyspace hits/misses.
- Stampede rate (доля параллельных обновлений), refresh time.
- Stale served % и Freshness lag.
- Каталог игр: Hit Ratio ≥ 85%, TTFB P95 ≤ 150 мс (edge).
- API-справочники: Revalidation-hit ≥ 60%, P95 ≤ 200 мс.
- Redis: P99 операция ≤ 5 мс, evictions не более 1% в час.
12) FinOps: стоимость кеша
$/GB-месяц RAM vs $/RPS origin: посчитайте точку окупаемости.
Offload и egress: CDN+Redis снижают исходящий трафик из регионо-origin.
Image/WebP/AVIF и денормализации дают наибольшую экономию байт.
Лимитируйте «дорогие MISS»: аналитика «байты × MISS × регион».
13) Примеры (фрагменты)
13.1 Cache-Aside с single-flight (псевдокод)
python def get(key, ttl, loader):
val = redis. get(key)
if val: return val with single_flight (key): # only one updates val = redis. get (key) # double check if val: return val data = loader () # request to source redis. setex(key, ttl_with_jitter(ttl), serialize(data))
return data
13.2 Публикация инвалидации по событию
json
{
"event": "game. updated",
"game_id": "g123",
"affected": ["catalog:list:region=TR", "game:card:g123:"]
}
Консьюмер подписан на канал и делает `DEL`/`PUBLISH` соответствующим ключам/тегам.
13.3 Ключ с версией схемы и локалью
game:card:v2:id=g123 region=BR currency=BRL lang=pt-BR
14) Чек-лист внедрения
1. Карта уровней кеша и TTL-матрица (статик/полустатик/API).
2. Нейминг ключей: домен, версия схемы, локаль/регион/валюта, tenant.
3. Выбор паттерна per-endpoint (aside/read-through/write-through/back).
4. SWR/SIE, single-flight и TTL-джиттер против stampede.
5. Инвалидация событиями (pub/sub), tag-purge для групп.
6. Near-cache для «горячих» ключей и prewarm перед пиками.
7. Форматы и компрессия (protobuf/MsgPack, Brotli), контроль размера.
8. Политики LRU/LFU, квоты на namespace/tenant.
9. SLO/метрики: hit ratio, latency, evictions, stale %, freshness lag.
10. Безопасность: no-store для персонального, токенизация, сеть/ACL.
15) Анти-паттерны
`no-cache` «на всякий случай» и отказы от TTL — нулевой offload.
Ключ включает все query/заголовки → взрыв кардинальности.
Массовые purge «всего CDN/Redis» при каждом релизе.
Отсутствие защиты от stampede и единовременное истечение «топ-ключей».
Единый общий Redis без квот/изоляции; «горячий» tenant съедает весь кеш.
Кеширование персональных ответов на edge/CDN.
Нет телеметрии freshness/evictions → слепое управление.
16) Контекст iGaming/финтех: практические ноты
Лидерборды/рейтинги: TTL 2–10 с, aggregate-потоки + CRDT, SWR при сбоях.
Каталог игр/баннеры: CDN+Redis; ключ: регион/валюта/язык; инвалидация по тегам «promo:update».
Платежные статусы: без кэша на пути записи; чтение — короткий TTL (≤3–5 с) или прямой запрос.
KYC/AML ответы: кешируйте non-PII деривативы (статусы), не храните изображения/документы в Redis.
VIP-путь: отдельный namespace/пул Redis, приоритетное обслуживание.
Итог
Сильная кеш-стратегия — это архитектура уровней, правильные паттерны обновления, продуманная TTL/инвалидация, устойчивость к stampede, аккуратные ключи и версии, а также наблюдаемость и FinOps. Следуя этим принципам, вы стабилизируете хвосты P95/P99, сократите нагрузку на источники и получите предсказуемую стоимость миллисекунды — именно там, где это важнее всего для продукта и бизнеса.