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 і спостерігайте р95/р99/вартість.

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).

Натискаючи кнопку, ви погоджуєтесь на обробку даних.