GH GambleHub

Архитектура кеширования: Redis, Memcached

Архитектура кеширования: Redis, Memcached

1) Когда и зачем кеш

Цели: снизить латентность, разгрузить БД/PSP/внешние API и смягчить пики.
Слои кешей часто многоуровневые: in-process (L1) → service-level (Redis/Memcached L2) → edge/CDN. Внутренний кеш ускоряет «горячие» чтения, L2 — общий для сервисов, edge — для публичного контента.

2) Redis vs Memcached — кратко

КритерийRedisMemcached
Типы данныхСтроки, хэши, списки, множества, zset, битмапы, HyperLogLog, Streams, Bloom/CF (через модули)Ключ-значение (строки)
ПерсистентностьAOF/RDB, no-fsync-loss при настройкеНет
КластерRedis Cluster (шардинг, failover) / SentinelКлиентский шард, без нативного failover
Транзакции/скриптыMULTI/EXEC, EVAL (Lua), атомарные операцииНет
TTL/evictionТонкая настройка политикБазовые
Латентность/памятьЧуть больше overheadОчень легкий, предсказуемый

Правило: если нужны сложные структуры данных, персистентность, pub/sub/стримы/скрипты — берите Redis. Если супер-простая, быстрая, дешевая KV-кеш-прослойка без durability — Memcached.

3) Шаблоны кеширования

3.1 Cache-aside (lazy)

Приложение читает из кеша → промах → читает из БД → кладет в кеш с TTL.

Простой контроль TTL, независимость от кеша. − Возможен «шторм» при промахах.

3.2 Read-through

Клиент/прокси сам подтягивает из origin при промахе и кладет в кеш.

Централизованная логика. − Сложнее инфраструктурно.

3.3 Write-through / Write-behind

Write-through: запись сначала в кеш, затем в БД.
Write-behind: запись в очередь, асинхронный флаш в БД (потенциальная потеря при краше — нужен журнал).

3.4 Two-tier (L1+L2)

L1 (in-process) c коротким TTL и soft TTL, L2 (Redis/Memcached) — «истина кеша». Инвалидация через pub/sub.

4) TTL, штормы и консистентность

TTL: задавайте близко к частоте изменения данных. Для горячих ключей используйте рандомизацию TTL (jitter): `ttl = base ± rand(0..base0.1)` — снимает синхронные истечения.

Dogpile (thundering herd): защитите промахи:
  • Singleflight: только один процесс перегенерирует значение (см. пример Lua).
  • Soft-TTL + background refresh: после `soft_ttl` отдавайте устаревшее (stale) и обновляйте фоном.
  • Semaphore/lock: `SET key:lock value NX PX=2000`.
  • Near-stale: `stale-while-revalidate` для API ответов (см. раздел 8).

5) Ключи, неймспейсы, сериализация

5.1 Именование ключей

Шаблон: `{domain}:{entity}:{id}:{field}`

Примеры:
  • `user:profile:42` `catalog:product:1001:v2` `psp:rates:2025-11-03`

Добавляйте версию схемы (`:v2`) — это облегчает массовую инвалидацию.

5.2 Неймспейсы через «версию пространства»

Держите ключ `ns:catalog = 17`. Реальные ключи: `catalog:17:product:1001`. Для глобальной инвалидации каталога просто инкрементируйте `ns:catalog`.

5.3 Сериализация/компрессия

JSON — удобен, но тяжел. Используйте MessagePack/CBOR.
Включайте компрессию (LZ4/ZSTD) для больших payload (>1–2 KB). В Redis — на стороне клиента.

6) Горячие ключи и шардирование

Hot-keys: мониторьте top-N по hit/miss/byte. Для экстремально горячих ключей:
  • Replicated read pattern: дублируйте значение в несколько shard-ключей `hot:k:1..N`, выбирайте случайный при чтении.
  • Local L1: держите в памяти процесса с подпиской на инвалидацию.
Шардирование:
  • Redis Cluster — нативно (16384 hash-слотов).
  • Memcached — клиентский консистентный хеш.
  • Hash-tag в Redis `{…}` фиксирует слот для набора ключей: `user:{42}:profile` и `user:{42}:limits` окажутся на одном шарде.

7) Политики вытеснения и размеры

Redis `maxmemory-policy`: `allkeys-lru`, `volatile-lru`, `allkeys-lfu`, `noeviction` и т. д. Для кеша обычно `allkeys-lru`/`allkeys-lfu`.
Memcached — LRU на item-slab.
Размер ключа и value: следите за max item size (Memcached по умолчанию 1 МБ, тюнинг slab).
Превышение памяти должно деградировать предсказуемо: не `noeviction` на активном пути.

Redis конфиг (фрагмент):

maxmemory 32gb maxmemory-policy allkeys-lfu hz 50 tcp-keepalive 60

8) Паттерны защиты от штормов — код

8.1 Redis Lua singleflight (псевдо)

lua
-- KEYS[1] = data_key, KEYS[2] = lock_key
-- ARGV[1] = now_ms, ARGV[2] = soft_ttl_ms, ARGV[3] = hard_ttl_ms, ARGV[4] = lock_ttl_ms local payload = redis. call("GET", KEYS[1])
if payload then local meta = redis. call("HGETALL", KEYS[1].. ":meta")
local last = tonumber(meta[2] or "0")
if tonumber(ARGV[1]) - last < tonumber(ARGV[2]) then return { "HIT", payload }
end if redis. call ("SET," KEYS [2], "1," "NX," "PX," ARGV [4]) then return {"REFRESH," payload} - one worker updates, the rest give stale end return {"STALE," payload}
end if redis. call("SET", KEYS[2], "1", "NX", "PX", ARGV[4]) then return { "MISS", nil }
end return { "BUSY", nil }

8.2 Node.js cache-aside (упрощенно)

js const v = await redis. get(key);
if (v) return decode(v);
const lock = await redis. setNX(key+":lock", "1", { PX: 1500 });
if (lock) {
const fresh = await loadFromDB(id);
await redis. set(key, encode(fresh), { EX: ttl, NX: false });
await redis. del(key+":lock");
return fresh;
} else {
await sleep(60);           // short backoff const retry = await redis. get (key) ;//give someone's already filled return decode (retry);
}

9) Инвалидация и согласованность

По событию: при изменении в БД публикуйте `pub/sub` событие `invalidate:{ns}:{id}` → подписчики удаляют ключи.
По таймеру: короткий TTL для часто меняющихся данных.
Версионирование: см. `ns:` ключи.
Outbox: гарантия доставки инвалидации (событие в лог/топик, ретраи).
Идемпотентность операций с кешом: используйте `SETXX/SETNX`, версии (`etag`) и хэш-поля для инкремента.

10) Репликация, кластер, failover

10.1 Redis

Sentinel: автоматический failover master-replica (стейтFUL IP/имя).
Cluster: шардирование + автоматический failover; клиенты должны поддерживать редиректы `MOVED/ASK`.
AOF/RDB: для кеша обычно `appendfsync everysec`, можно без персистентности (как чистый кеш).

10.2 Memcached

Нет репликации из коробки. Надежность — через многосерверный шард + повтор `n` (client-side).
При падении ноды — рост промахов и «переучивание» кеша.

10.3 K8s и сетевые аспекты

Redis/Memcached не любят частую пересоздачу pod’ов; используйте StatefulSet + антиподы по AZ, фиксированные PVC/POD IP.
Ставьте PodDisruptionBudget и TopologySpreadConstraints.

11) Транзакции, скрипты и атомарность (Redis)

INCR/DECR, HINCRBY — счетчики, квоты, rate-limits (только учитывайте persist).
MULTI/EXEC — пачка атомарных команд.
Lua (EVAL) — read-modify-write без гонок.
Pipeline — уменьшает RTT (особенно при сетевом хопе).

Пример rate-limit (token bucket, упрощенно):
lua
-- KEYS[1]=bucket, ARGV[1]=capacity, ARGV[2]=refill_rate_per_sec, ARGV[3]=now_ms
-- Returns 1 if the token is issued, otherwise 0

12) Очереди, pub/sub и Streams (Redis)

Pub/Sub: инвалидация, сигналы. Без сохранения, только онлайн-слушатели.
Streams: очереди событий с подтверждением (ACK), группы потребителей, ретраи — удобно для write-behind/фан-аутов.
Lists (`BRPOP`): простые очереди.
Не используйте Redis как «единую шину всего» без бэкапа — это кеш/быстрая шина, не Kafka.

13) Безопасность и доступ

Изоляция сетей/VPC, mTLS на ingress-уровне, ACL/пароли (`requirepass`/ACL в Redis 6+).
Disable dangerous команд в Redis (`CONFIG`, `FLUSHALL`, `KEYS`) через ACL.
Для Memcached — не слушать публичные интерфейсы, `-U 0` (без UDP), только приватные сети.
PII не хранить; если нужно — короткий TTL + шифрование на уровне приложения.

14) Наблюдаемость и обслуживание

Ключевые метрики:
  • Hit ratio / Miss ratio (по namespace/маршруту).
  • Latency p95/p99 команд `GET/SET/MGET`, timeouts.
  • Evictions и OOM errors.
  • Replication lag (Redis), cluster state, migrate/rehash events.
  • Top-N keys по трафику/байтам (сэмплингом).
  • Логи: медленные команды (`slowlog`), ошибки сети.
  • Дашборды: общее (CPU/RAM/connections), команды, слоты кластера, sentinels, пропуская через Prometheus-экспортеры.

15) Конфиги и развертывание — примеры

15.1 Redis Sentinel (фрагмент)


port 6379 protected-mode yes appendonly yes appendfsync everysec maxmemory-policy allkeys-lfu
`sentinel.conf`:

sentinel monitor m1 10. 0. 0. 11 6379 2 sentinel auth-pass m1 sentinel down-after-milliseconds m1 5000 sentinel failover-timeout m1 60000

15.2 Redis Cluster (helm values, упрощенно)

yaml cluster:
enabled: true nodes: 6  # 3 masters + 3 replicas persistence:
size: 100Gi resources:
requests: { cpu: "500m", memory: "2Gi" }

15.3 Memcached (deployment)

yaml containers:
- image: memcached:1. 6 args: ["-m", "32768", "-I", "2m", "-v", "-t", "8", "-o", "modern"]
ports: [{ containerPort: 11211 }]

15.4 NGINX как read-through прокси (контур API)

nginx proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api:100m max_size=10g inactive=10m;
map $request_uri $cache_key { default "api:$request_uri"; }
location /api/ {
proxy_cache api;
proxy_cache_valid 200 1m;
proxy_cache_use_stale updating error timeout http_500 http_502 http_503 http_504;
proxy_cache_lock on;      # singleflight на уровне NGINX proxy_cache_key $cache_key;
proxy_pass http://backend;
}

16) Тестирование и гейты

Нагрузочные профили «холодный/теплый/горячий кеш».
Инъекция промахов (purge массово) — origin должен выдержать «переучивание».
Алерты: резкое падение hit-ratio, рост miss latency, лавина evictions, рост timeouts.

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

Хранить «истину» в Redis без AOF/RDB и без резервирования.
TTL=0 (бессрочно) для волатильных данных → вечная неконсистентность.
Массовый `KEYS ` в проде.
Отсутствие jitter/soft-TTL → синхронные истечения и шторма.
Один инстанс на все команды без шардирования/реплик.
Использовать Memcached для задач, требующих атомарности/скриптов.

18) Чек-лист внедрения (0–45 дней)

0–10 дней

Выбрать шаблон (cache-aside + L1/L2), описать ключи, TTL, неймспейсы.
Включить jitter/soft-TTL, singleflight; базовые алерты/дашборды.
Для Redis — настроить ACL, protected-mode, slowlog, maxmemory-policy.

11–25 дней

Перейти на шардирование (Redis Cluster или клиентский хеш), реплики.
Инвалидация через pub/sub или версию неймспейса; outbox в БД.
Нагрузочные тесты «переучивания» кеша; limiting origin.

26–45 дней

Автопромо/канареечные TTL, прогрев перед релизом.
Streams для write-behind/фоновых пересборок.
Еженедельные отчеты по hit-ratio, топ-ключам, стоимости памяти.

19) Метрики зрелости

Hit-ratio L2 ≥ 80% (статистика по маршрутам/неймспейсам).
P95 GET < 2–3 ms (in-DC), промахи < SLO origin.
0 шторма при массовой инвалидации (доказано тестами).
Автоматическая инвалидация и версионирование неймспейсов.
Шардирование/репликация покрывают отказ 1 узла без заметной деградации.

20) Заключение

Сильная архитектура кеша — это дисциплина ключей и TTL, защита от шторма, правильное шардирование и предсказуемое вытеснение. Redis дает богатую семантику, персистентность и атомарность; Memcached — максимум простоты и скорости. Добавьте наблюдаемость, инвалидацию по событиям, L1+L2, и кеш станет ускорителем платформы, а не источником случайных падений и «мистических» багов.

Contact

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

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

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

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

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

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