# Балансировка нагрузки и кэширование

← [Раздел](README.md) · [Главная](../README.md)

## Цель

Понять роль **load balancer**, алгоритмы распределения, **кэширование** на разных уровнях и типичные ошибки инвалидации кэша.

## Предварительно

- [Stateless vs stateful](stateless-vs-stateful.md)
- [Базы и кэш](../02-komponenty/bazy-i-hranilishcha.md)

## Время

**3–5 часов**

---

## Load Balancer (LB)

**Балансировщик** распределяет входящие запросы между **здоровыми** backend-инстансами.

```mermaid
flowchart TB
  C1[Клиенты] --> LB[Load Balancer]
  LB --> S1[Server 1]
  LB --> S2[Server 2]
  LB --> S3[Server 3]
```

| Тип | Где |
|-----|-----|
| L4 (TCP) | Быстрый, не смотрит HTTP |
| L7 (HTTP) | URL routing, headers, TLS |
| DNS LB | Геораспределение (упрощённо) |

В K8s: **Service** (ClusterIP/LoadBalancer) + **Ingress**.

---

## Алгоритмы балансировки

| Алгоритм | Поведение |
|----------|-----------|
| Round robin | По очереди |
| Least connections | Кому меньше активных conn |
| IP hash | Один клиент → один backend (sticky) |
| Weighted | Мощным серверам больше трафика |

Для stateless API — **round robin** или **least connections**.

---

## Health checks

LB опрашивает backends:

```http
GET /health HTTP/1.1
Host: api.internal

200 OK → в пуле
503 → исключён
```

Без health check трафик идёт на **мёртвый** pod — 502 для пользователей.

---

## Кэш — зачем

| Проблема | Кэш снижает |
|----------|-------------|
| Повторные GET одних данных | Нагрузку на БД |
| Тяжёлые агрегации | CPU API |
| Статика (JS, CSS) | Latency |

**Cache hit ratio** — ключевая метрика (цель 80%+ для hot data).

---

## Уровни кэширования

```mermaid
flowchart LR
  User[Браузер] --> CDN[CDN]
  CDN --> LB[LB]
  LB --> API[API]
  API --> Redis[Redis]
  Redis -->|miss| DB[(PostgreSQL)]
```

| Уровень | Что кэшируют | TTL |
|---------|--------------|-----|
| Browser | Cache-Control headers | минуты–дни |
| CDN | Статика, иногда API GET | настраивается |
| Application (Redis) | Списки, профили | секунды–часы |
| DB query cache | Редко вручную | ORM second-level |

---

## Паттерны кэша в приложении

| Паттерн | Описание |
|---------|----------|
| Cache-aside | App читает cache → miss → DB → write cache |
| Read-through | Cache сам грузит из DB |
| Write-through | Запись в cache и DB вместе |
| Write-behind | Запись в cache, async в DB (рискованно) |

**Cache-aside** — самый частый для Redis.

```text
GET key:
  if redis.exists(key): return redis.get(key)
  val = db.query(...)
  redis.set(key, val, TTL=60)
  return val
```

---

## Инвалидация кэша

| Стратегия | Когда |
|-----------|-------|
| TTL | Данные слегка устаревшие ок |
| Delete on write | После UPDATE списка — `DEL list:42` |
| Version key | `list:42:v3` при изменении |

**Проблема:** забыли инвалидировать — пользователи видят старое. Тестируйте write path.

---

## Кэширование HTTP

```http
Cache-Control: public, max-age=3600
ETag: "abc123"
```

| Тип ресурса | Рекомендация |
|-------------|--------------|
| Статика с hash в имени | `max-age=31536000, immutable` |
| Персональный API GET | `private` или не кэшировать на CDN |
| POST/PUT/DELETE | Никогда не кэшировать |

---

## Thundering herd

Кэш истёк — **1000** запросов одновременно бьют в БД.

| Мера | Суть |
|------|------|
| Lock | Один запрос обновляет, остальные ждут |
| Stale-while-revalidate | Отдать старое, обновить фоном |
| Jitter TTL | Не все ключи истекают в одну секунду |

---

## CDN для глобальных пользователей

Edge-серверы ближе к пользователю:

| Кэшируется | Не кэшируется без настройки |
|------------|------------------------------|
| JS, CSS, images | Персональные JSON API |
| Публичные страницы | Set-Cookie ответы |

**Учебный список** — персональные данные: CDN для **статики приложения**, не для API с JWT.

---

## LB + кэш + scale: целевая схема

```mermaid
flowchart TB
  Users[Users] --> CDN[CDN static]
  Users --> LB[LB]
  LB --> API1[API]
  LB --> API2[API]
  API1 --> Redis[(Redis)]
  API2 --> Redis
  Redis --> DB[(PostgreSQL primary)]
  DB --> Replica[(Read replica)]
  API1 --> Replica
  API2 --> Replica
```

Read-heavy: GET lists → replica; write → primary.

---

## Метрики

| Метрика | Тревога |
|---------|---------|
| `cache_hit_ratio` | < 50% для hot keys |
| `lb_healthy_hosts` | < desired |
| `db_connections` | near max |
| `p95 latency` | > NFR |

---

## Практика

1. Cache-aside псевдокод для GET `/lists/{id}`.  
2. Какие заголовки для `app.js` с hash в имени файла?  
3. Пять узких мест 100→10k users — заполните меры (КТ-3).

---

## Самопроверка

1. L7 LB vs L4 — в чём разница?  
2. Что такое cache-aside?  
3. Почему персональный API не кладут на публичный CDN без оговорок?

---

## Дальше

→ [07 — Надёжность](../07-nadezhnost/README.md)
