GH GambleHub

Стратегии кеширования

1) Зачем кешировать и где это делать

Кеш — это слой быстрой памяти, уменьшающий латентность и нагрузку на дорогие ресурсы (CPU/БД/внешние API). Важные цели:
  • Скорость (p95/p99 ниже), стоимость (меньше egress/CPU), устойчивость (меньше зависимостей под пиком).
  • Сглаживание пиков и изоляция от “шумных соседей”.
Типовые уровни:

1. Клиент (браузер/мобильный) — HTTP-кеш, IndexedDB, local storage.

2. Edge/CDN — POP-узлы ближе к пользователю, кэшируют статику и часть API.

3. L7-шлюз/Reverse-proxy — Nginx/Envoy/Varnish (микрокеш, SWR).

4. Сервисный кэш — Redis/Memcached внутри кластера.

5. Внутрипроцессный — in-memory (Caffeine/Guava/LRU-map).

6. Кэш в БД — материальные представления, вторичные индексы.

Правило: кешируйте как можно ближе к потребителю, но истину храните единожды.

2) Паттерны кеширования

2.1 Cache-aside (“lazy loading”)

Приложение сначала читает из кэша; при промахе — из источника, затем пишет в кэш.
Плюсы: простота, контроль. Минусы: холодные старты, окна рассогласования.

2.2 Read-through

Чтение всегда через кэш, который сам ходит к источнику при промахе (библиотека/проксирующий слой).
Удобно централизовать политики TTL/серилизацию.

2.3 Write-through / Write-back (write-behind)

Write-through: запись в кэш и источник синхронно → согласованность выше, латентность выше.
Write-back: запись в кэш, асинхронная флеш-запись в источник → быстро, но риски потерь и конфликтов.

2.4 Refresh-ahead (proactive)

Предсказывает “скоро истечет TTL” и обновляет ключ в фоне, предотвращая stampede.

2.5 Negative caching

Кеширование “нет данных/404/пусто” на короткий TTL снижает нагрузку на источник.

2.6 Micro-caching

Очень короткие TTL (0.5–5 с) на L7 для “почти динамики” (списки, главная) — резко снижает хвосты.

3) HTTP-кеш: заголовки и контроль

3.1 Базовые заголовки

`Cache-Control`: `max-age`, `s-maxage` (для shared кэшей), `public/private`, `no-store`, `stale-while-revalidate`, `stale-if-error`.
Валидаторы: `ETag` (хэш содержимого), `Last-Modified`.
Запросы с условиями: `If-None-Match`, `If-Modified-Since` → 304 Not Modified.

3.2 Vary и ключи

`Vary: Accept-Encoding, Authorization, Cookie, Accept-Language` — формирует разные варианты кэша. Минимизируйте `Vary`, чтобы не “взорвать” кардинальность.

3.3 Пример HTTP-ответа


Cache-Control: public, max-age=60, s-maxage=300, stale-while-revalidate=60
ETag: "a1b2c3"
Vary: Accept-Encoding

4) Проектирование ключей и TTL

4.1 Ключи

Структурируйте: `tenant:user:{id}:profile:v3` (включайте версию схемы).
Избегайте PII в ключе.
Для коллекций — ключ + параметры запроса (нормализованные и отсортированные).

4.2 TTL и согласованность

Короткий TTL снижает рассогласование, но увеличивает промахи.
Для критичных данных — валидаторы (`ETag`) и SWR (stale-while-revalidate).
Для редко меняющихся — долгий TTL + “бомбочки” инвалидации.

4.3 Версионирование/бастинг

При несовместимых изменениях — меняйте префикс/версию ключа (`v2 → v3`).
Для статических ресурсов — content hash в имени файла.

5) Инвалидация: стратегии и практики

5.1 Прямое удаление

`DEL key` / `PURGE` на прокси. Опасность: гонки между удалением и многократными читателями.

5.2 Тэги/Surrogate keys

Связывайте документ с набором тегов (категория/автор). Инвалидация — по тегу.
В Varnish/Edge — `Surrogate-Key: article:42 tag:author:7` + `BAN tag:author:7`.

5.3 Event-driven инвалидация

Pub/Sub (Kafka/NATS): при изменении источника — публикуем событие “invalidate”.
Консьюмеры кэша слушают и удаляют/обновляют ключи.

5.4 Двуфазная

Сначала помечаем ключ устаревшим (soft TTL), обслуживаем stale, в фоне обновляем и атомарно заменяем.

6) Борьба со stampede/dogpile и горячими ключами

6.1 Request coalescing (singleflight)

Один продюсер обновляет ключ, остальные ждут результат (мьютекс/лейбл “обновляется”).

6.2 Jitter к TTL

Добавляйте случайность (±10–20%) к TTL, чтобы избежать синхронного протухания.

6.3 Soft-TTL + hard-TTL

До soft-TTL обслуживаем из кэша, параллельно триггерим refresh; по hard-TTL — считаем промах.

6.4 Горячие ключи

Локальные кэши поверх общих (two-tier).
Репликация горячего ключа в несколько шардов и рандомный выбор (только для read-only).
Rate limit на обновление конкретного ключа.

6.5 Пример Redis + Lua (singleflight-эскиз)

lua
-- SETNX lock with TTL to avoid deadlocks local ok = redis. call("SET", KEYS[1], "1", "NX", "EX", ARGV[1])
if ok then return "LOCKED"
else return "WAIT"
end

7) Политики вытеснения и прием в кэш

7.1 Eviction

LRU: просто и хорошо для локальности.
LFU: лучше при “долгоживущих” горячих ключах.
ARC/TinyLFU: баланс recency/frequency.

7.2 Admission (впуск)

Не пускайте гигантские редкие объекты (TinyLFU/Bloom-фильтры).
Компрессия больших значений (LZ4/Zstd) на границе “размер/латентность”.

8) Шардирование и топологии

8.1 Consistent hashing

Стабильно распределяет ключи по нодам, уменьшает перемещения при росте/сжатии кластера.

8.2 Топологии Redis/Memcached

Redis Cluster (слоты/шарды), Sentinel (фейловер), репликация read-only.
Memcached — клиент-сайд шардинг (ketama hashing), без репликации на уровне сервера.

8.3 Локальный + распределенный

Каскад: in-proc (микро-TTL/LRU) → Redis (TTL длиннее) → источник.
Будьте осторожны с двоеточиями TTL и кэш-валидаторами.

9) Edge, CDN и L7-кеш

9.1 Micro-cache на Nginx

nginx proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api:100m inactive=10m;
map $request_method $skip_cache { default 0; POST 1; PUT 1; DELETE 1; }

server {
location /api/list {
if ($skip_cache) { add_header Cache-Control "no-store"; }
proxy_cache api;
proxy_cache_valid 200 2s;       # micro-cache proxy_cache_use_stale error timeout updating;
proxy_cache_background_update on;   # SWR add_header X-Cache $upstream_cache_status;
proxy_pass http://upstream;
}
}

9.2 Envoy (SWR и условия)

yaml http_filters:
- name: envoy. filters. http. cache typed_config:
"@type": type. googleapis. com/envoy. extensions. filters. http. cache. v3. CacheConfig typed_config:
"@type": type. googleapis. com/envoy. extensions. http. cache. file_system_http_cache. v3. FileSystemHttpCacheConfig cache_path: "/var/cache/envoy"

9.3 Varnish (Surrogate keys)

Используйте `Surrogate-Key` и `ban` по тегам для пакетной инвалидации.

10) Кеш и согласованность данных

10.1 Read-your-writes

Для пользовательских профилей/корзины обеспечьте либо короткие TTL, либо запись сквозь кэш (write-through), либо маркировку клиента (bypass на N секунд после записи).

10.2 Eventual vs Strong

Для рекомендательных/аналитических — eventual + длинный TTL.
Для денег/статусов заказов — короткий TTL, валидация, иногда без кэша на критических путях.

10.3 Инварианты

Не кешируйте поля, влияющие на безопасность/ACL, без строгих TTL и повторной проверки.

11) Наблюдаемость, SLO и управление

11.1 Метрики

hit_ratio (общий и per-route), byte_hit_ratio, miss_rate.
stampede_prevented_total, refresh_ahead_total, ban/purge_total.
Латентность: p50/p95/p99 из кэша vs из источника.
hot_keys_topN и их QPS/байты.

11.2 Логи и трассировки

Логируйте `X-Cache: HIT/MISS/STALE/UPDATING`.
В трейсах отмечайте источник ответа (`cache=true`, `tier=edge|service|local`).

11.3 SLO-подход

Пример: “для API /catalog p99 ≤ 250 мс, cache hit ≥ 85%, stampede ≤ 0.1% запросов”.

11.4 Runbooks

“Промахи растут” → проверить TTL, прогрев/инвалидации, hot-keys, размер кэша и политику принятия.

12) Безопасность и мульти-тенантность

Встраивайте tenant-id в ключи (и в `Vary` при HTTP).
Не кешируйте приватные ответы как `public`.
Шифруйте кэш с чувствительными данными или храните только не-PII/ID.

13) Типовые рецепты

13.1 Каталог/лента (почти динамика)

Edge-микрокеш 1–3 с + SWR, внутри — Redis на 15–60 с, инвалидация по событиям обновления.

13.2 Профиль пользователя

Cache-aside с TTL 30–120 с, bypass 5–10 с после обновления профиля (cookie/хедер), либо write-through.

13.3 Валюто-курсы/справочники

Длинные TTL (минуты-часы) + целевая инвалидация при публикации новых данных; `ETag` для условных GET.

13.4 Поисковая выдача

Edge-микрокеш 1–2 с, внутри — refresh-ahead и coalescing, нормализация query-параметров в ключе.

14) Анти-паттерны

Кэш без инвалидации: надежда только на TTL → долгие окна неактуальности.
Гигантский `Vary`: “взрыв” вариантов → низкий hit-rate.
Единый кэш для prod/experiments → загрязнение.
Нет защиты от stampede → пики на источнике при истечении TTL.
Кэш денег/прав/ACL без строгих гарантий.
Компрессия “всего подряд” — лишние CPU, ухудшение p99 на мелких объектах.

15) Чек-лист внедрения

  • Определите уровни кеша и их цели (edge/service/local).
  • Спроектируйте ключи (версионирование, tenant, нормализация параметров).
  • Выберите паттерн (cache-aside/read-through/refresh-ahead).
  • Настройте TTL/soft-TTL/jitter, включите SWR.
  • Реализуйте coalescing/singleflight, защиту от stampede.
  • Организуйте инвалидацию (события, теги, purge/ban).
  • Введите метрики hit-ratio/латентности и дашборды `X-Cache`.
  • Проведите нагрузочные тесты с горячими ключами.
  • Пропишите SLO и runbooks.
  • Проверьте безопасность/tenant-изоляцию и `Vary`.

16) FAQ

Q: Что выбрать — cache-aside или read-through?
A: Для простых сервисов — cache-aside. Нужна централизация и единая политика — read-through.

Q: Как понять оптимальный TTL?
A: Отталкивайтесь от допустимой устарелости, частоты обновлений и целевого hit-rate; добавьте jitter и наблюдайте p95/p99/стоимость.

Q: Когда уместен write-back?
A: Для высоконагруженных потоков, где приемлема eventual-консистентность и есть надежная очередь/лог для “дописывания”.

Q: Можно ли кешировать авторизованные ответы?
A: Да, но помечайте `private` и/или включайте tenant/user в ключ/`Vary`. Для truly-private — клиентский кэш.

Q: Как прогревать кэш?
A: Списки популярных ключей, бэкграунд-вормер, реплай с логов, прогрев перед релизом/пиком (черная пятница и т.п.).

17) Итоги

Эффективное кеширование — это дизайн ключей + разумные TTL + грамотно выбранный паттерн, усиленные инвалидацией по событиям, SWR/refresh-ahead и защитой от stampede. Разнесите кэш по уровням (клиент/edge/сервис), добавьте наблюдаемость и SLO — и получите стабильные хвосты латентности, предсказуемую стоимость и устойчивость к пиковым нагрузкам.

Contact

Свяжитесь с нами

Обращайтесь по любым вопросам или за поддержкой.Мы всегда готовы помочь!

Telegram
@Gamble_GC
Начать интеграцию

Email — обязателен. Telegram или WhatsApp — по желанию.

Ваше имя необязательно
Email необязательно
Тема необязательно
Сообщение необязательно
Telegram необязательно
@
Если укажете Telegram — мы ответим и там, в дополнение к Email.
WhatsApp необязательно
Формат: +код страны и номер (например, +380XXXXXXXXX).

Нажимая кнопку, вы соглашаетесь на обработку данных.