Job 2026 md

title: Статья 03 — Масштабирование с нуля: LB, CDN, stateless source: подготовлено 2026-09-17, TASK-45.43; каркас — brief.md §03; развёртка knowledge-base.md §3, §13.1–13.2, §13.8; источники — Alex Xu гл. 1 (TASK-45.38), System Design Primer (TASK-45.7), эталон classic-designs.md §1


Статья 03: масштабирование с нуля — LB, CDN, stateless

Третья статья серии — путь «один сервер → миллионы пользователей». Критерий «умею, если» из брифа: могу объяснить, что и в каком порядке добавляю при росте нагрузки и почему. Это первая статья «про коробки»: фреймворк (01-interview-framework.md) задал, как вести беседу, оценки (02-estimations.md) — какими числами обосновывать выбор; здесь — какие блоки появляются на схеме, в каком порядке и чем за каждый платим.

Место раздела в каркасе: строительные блоки этапа 3 (крупноблочная схема рисуется из них) и материал для этапа 4 (детализация) и этапа 6 (отказы этих блоков). Контекст нашей секции (Yandex Codenv, этап 2): задача уровня «инстаграм/твиттер» не предполагает рисовать эволюцию — схему сразу рисуем как результат роста (LB → stateless-сервисы → кэш → реплицированная БД → CDN → очередь). Но за каждым блоком должен стоять ответ «какая боль это лечит»: интервьюер проверяет не знание компонентов, а понимание, зачем каждый нужен и чем платим. Лестница «боль → шаг» — способ держать этот ответ наготове.

1. Словарь: два трейдоффа и два направления

1.1 Performance vs scalability, latency vs throughput

Пара определений из System Design Primer, на которых строится язык всего раздела:

  • Performance vs scalability. «Проблема производительности — система медленная для одного пользователя; проблема масштабируемости — быстрая для одного, но медленная под нагрузкой». Масштабируемая система — та, чья производительность растёт пропорционально добавленным ресурсам (определение CTO AWS). На секции различать обязательно: «медленный запрос» и «не тянет 50k RPS» лечатся разными средствами (индекс/профилирование vs масштабирование).
  • Latency vs throughput. Латентность — время одного действия, throughput — число действий в единицу времени. Цель формулируем именно так: максимальный throughput при приемлемой latency — не наоборот. Балансировка, кэш, CDN и очереди — четыре способа двигаться в этом поле, и у каждого своя цена.

Сюда же базовая пара доступность/консистентность: горизонтальный масштаб множит копии, а копии — это уже вопрос согласования (раздел 09). Пока фиксируем лексику: любое решение этого раздела проговаривается как «получаю …, плачу …».

1.2 Вертикальное vs горизонтальное

Вертикальное (scale up) — мощнее железо: CPU, RAM, диск одного сервера. Горизонтальное (scale out) — больше серверов в пуле.

Вертикальное отлично для старта и малого трафика: его главное преимущество — простота (не трогаем архитектуру вообще). Два фатальных ограничения: у одного сервера жёсткий лимит ресурсов — их нельзя наращивать бесконечно; и вертикальное масштабирование не даёт отказоустойчивости — сервер один, упал он, упал сервис целиком.

Горизонтальное — стратегия для большого масштаба, и это не вкус, а физика: такты процессора замерли на ~3 ГГц с 2005 года, растут ядра, а не герцы (тренды железа — статья 02 §3.4). Но горизонтальное масштабирование не бывает бесплатно: оно требует, чтобы копии сервера были взаимозаменяемы, то есть не держали стейт (§5), и требует балансировщика (§3). Это и есть центральная связка раздела: горизонтальный масштаб = stateless + LB.

2. Лестница эволюции: «боль → шаг»

Канон раздела — глава 1 Alex Xu: система растёт итеративно, каждый компонент — ответ на конкретную боль, а не «сразу микросервисы на Kafka». Десять шагов, каждый с ценой:

# Боль (сигнал) Шаг Что получаем Чем платим
0 Один сервер: web + БД + всё на одной машине Старт за вечер SPOF целиком; масштабируется только железом
1 CPU web забит, БД душится на тех же ресурсах Разделить web и БД Слои масштабируются независимо Два сервера, сеть между ними
2 Одни и те же тяжёлые запросы к БД Кэш в приложении Минус повторные чтения Память процесса, инвалидация
3 Один web-сервер: падает — всё падает, пика не тянет LB + пул web-серверов Отказоустойчивость и рост web-слоя Сам LB не должен стать SPOF
4 Одна БД: чтение душит запись Репликация БД (master — запись, реплики — чтение) Read-масштаб + резерв Лаг репликации, read-your-writes (раздел 05)
5 Кэш в приложении не масштабируется между нодами Отдельный кэш-слой (Redis/Memcached) Общий кэш для всех нод Ещё система: TTL, вытеснение, инвалидация (раздел 06)
6 Статику тянет web-слой, глобальные пользователи далеко CDN Медиа ближе к пользователю, минус трафик с origin Деньги, TTL-стейл, зависимость от провайдера
7 Нельзя просто добавить web-сервер: сессии на нодах Stateless web-tier (стейт наружу) Автомасштабирование, любой сервер любой запрос Хранилище стейта (Redis/KV), +1 хоп
8 Пользователи на континентах, ДЦ один Несколько ДЦ + GeoDNS Латентность, живучесть региона Синхронизация данных, тесты, деплой (раздел 08)
9 Пик нагрузки роняет синхронный путь Очередь сообщений Асинхронная развязка, независимое масштабирование At-least-once, идемпотентность (раздел 07)

Как читать на секции. Верх лестницы (3–9) — то, из чего состоит «крупноблочная схема» твиттера: рисуем сразу её, а лестницу держим как обоснование: «LB здесь, потому что web-слой горизонтальный; web-слой горизонтальный, потому что stateless; сессии — в KV, не на нодах». Обратный порядок — «нарисовал коробку, теперь придумываю зачем» — интервьюер слышит мгновенно.

Порядок шагов не догма, а дефолт: числа могут менять последовательность. Если read/write = 100:1, репликация (шаг 4) и кэш (5) окупятся раньше LB; если в ответе «видео/фото» (статья 02 §7), CDN выходит на первый план, а web-слой остаётся скромным. Решает расчёт, не табличка — и это проговаривается: «порядок зависит от дефицитного ресурса, для медиа-сервиса сначала CDN и объектное хранилище».

3. Балансировщик нагрузки: три работы и алгоритмы

3.1 Зачем

Три эффекта, формулировка SDP: не слать трафик на нездоровые серверы, не перегружать живые, убрать единую точку отказа на web-уровне. Первый и третий — про доступность, второй — про throughput. Из схемы включения (Сюй): клиенты подключаются только к публичному IP балансировщика; серверы за ним общаются по внутренним IP, невидимым из интернета — «клиенты больше не имеют прямого доступа к веб-серверам», что заодно и безопасность. Отказ сервера 1 → трафик на сервер 2, в пул добавляется новый: для клиентов ничего не меняется.

3.2 Алгоритмы

Экзаменационный набор — ../knowledge-base.md §13.2:

Алгоритм Когда
Round Robin дефолт для stateless-нод
Weighted RR ноды разной мощности (старые/новые поколения)
Least Connections долгие соединения: websocket, streaming
Hash / IP-sticky сессии, кэш-локальность; вместе с consistent hashing — ответ про масштабирование пула
Random называем, что не выбираем

Обязательная фраза, без которой алгоритм не засчитывается: «перед LB — health checks, нездоровую ноду выбрасываем из ротации». Health check — не деталь, а механизм, который делает список эффектов реальным: LB смотрит не только «жив ли порт», но и готов ли сервис (деплой, прогрев, зависимость).

3.3 L4 vs L7 и бонусы на краю

  • L4 — решение по транспорту (IP + порт, NAT): дёшево, быстро, но «слепое» к содержимому. L7 — читает прикладной слой (путь, заголовки, куки, тело) и маршрутизирует осмысленно: «видео — на видеосерверы, биллинг — на усиленные». На современном железе цена L7 невелика, и дефолт секции — L7.
  • SSL termination на LB: бэкенды не тратят CPU на TLS и не держат сертификаты на каждой ноде. Внимание: TLS остаётся дорогим — дефицитный ресурс ноды часто именно CPU на handshake (эталон: ~20k RPS на воркер из-за новых TLS-handshake'ов, а не из-за логики).
  • Session persistence (sticky по куке) — есть у всех LB, но это костыль для stateful-приложения; правильный ответ — сделать слой stateless (§5), а не приклеивать пользователей к нодам.

3.4 LB сам не SPOF. И DNS как нулевой слой балансировки

Единый LB отменяет весь смысл схемы → LB живёт парой (active-passive или active-active). Перед LB стоит DNS — и это тоже слой балансировки: managed-DNS умеет weighted round robin (баланс кластеров разного размера, вывод серверов из-под трафика), latency-based и geolocation-based маршрутизацию; GeoDNS «ведёт в ближайший регион» — с этого начинается мульти-ДЦ (KB §13.1). Плата DNS: задержка lookup (смягчается кэшем), стейл-кэш при TTL, и сам DNS-провайдер как внешняя зависимость (DDoS на Dyn в октябре 2016 уложил Twitter у всех, кто не знал IP) — «не углубляться, но знать».

3.5 Что происходит вниз по стеку

Горизонтальный рост app-слоя умножает не только throughput: каждая новая нода открывает соединения к кэшам и БД — число соединений растёт как произведение, и его ограничивают пулом/прокси соединений (pgbouncer-класс). Проговаривать при росте пула — иначе интервьюер спросит «а БД выдержит 300 соединений?».

3.6 Эталон: LB в эталоне Яндекса

В URL shortener из статьи Яндекса (../classic-designs.md §1) балансировка решена IPVS: по балансировщику на ДЦ (маршрут не должен выходить за пределы ДЦ — иначе LB сам станет меж-ДЦ зависимостью), за ним воркеры. Воркер — это пара «веб-сервер + ко-локальный KV»: балансировщик раздаёт запросы нодам, а данные живут рядом с CPU (ко-локация по доминантному ресурсу). Итог этапа 5: 500k RPS ÷ 20k RPS на воркер (дефицит — TLS!) = 25 воркеров в пике, ×4 ДЦ с запасом = 36 воркеров + 4 IPVS + 3 СУБД = 43 сервера на 500k RPS. Это и есть «масштабирование числами»: LB не абстракция, а конкретный план ёмкости.

Сюда же смежное понятие reverse proxy (NGINX/HAProxy): фасад перед бэкендами — прячет внутренние сервисы, терминирует SSL, сжимает, кэширует, раздаёт статику. Отличие: LB осмыслен с несколькими серверами, reverse proxy полезен и с одним; на практике NGINX умеет оба режима сразу (L7). На секции достаточно: «на краю — reverse proxy / LB, единая точка терминирования TLS и health checks».

4. CDN: статику — ближе к пользователю

4.1 Когда

Сигнал из оценок (статья 02 §8): egress ≥ Gbps или медиа-контент → CDN с offload 90 %+. Противоположный пример там же: URL shortener отдаёт redirect-заголовки по ~200 байт — CDN не нужен, это не медиа. Двойной выигрыш CDN: пользователю — ближайший узел (120 мс до origin → 30 мс до края), нашим серверам — минус чужие запросы.

4.2 Как работает

Запрос image.png на домен провайдера → на краевом узле промах → CDN тянет с origin (наш сервер или S3), получает объект вместе с HTTP-заголовком TTL → кладёт в кэш края → следующий пользователь из этого региона получает объект с края, пока TTL жив. То есть CDN — это кэш с географией: к нему применимы все слова про инвалидацию и TTL из раздела про кэширование, только «серый» фон — провайдер.

Режимы (KB §13.8): push hot / pull rare / hybrid — горячее проталкиваем на край заранее, редкое тянем по промаху, обычно гибрид.

4.3 Нюансы — проговаривать все четыре

  1. Стоимость: трафик в/из CDN платный, кэшировать нечасто запрашиваемое бессмысленно — редкие объекты из CDN убираем.
  2. TTL-баланс: короткий — стейл меньше, но чаще ребайты с origin; длинный — наоборот. Для контента, зависящего от времени, выбираем осознанно.
  3. Отказ CDN: у клиента должен быть fallback — обнаружить недоступность края и запросить с origin. «CDN упал — упал сервис» не принимается.
  4. Инвалидация до истечения TTL: purge через API провайдера или версионирование URL (logo.jpg?v=2) — классический приём «новый объект = новое имя», снимающий проблему стейла целиком.

Метрики края: cache miss outs, active/total resources — привязаны к узлу, подаются на этапе 6 (KB §13.8).

5. Stateless: стейт покидает вычислительные ноды

5.1 Почему стейт на сервере — враг масштаба

Если сервер хранит состояние (сессии пользователя, его данные), запрос обязан прийти «на свой» сервер: аутентификация пользователя A пройдёт только на сервере 1, где лежит его сессия. Лечение на уровне LB — липкие сессии — работает, но платит оверхедом и ломает главное: добавление/удаление серверов становится трудным, отказ сервера — потерей его сессий. Stateful-слой не масштабируется горизонтально в принципе, только вертикально.

5.2 Правило

Вычислительные ноды — без состояния; состояние — в разделяемом хранилище. Любой сервер обрабатывает любой запрос, подтягивая стейт из общего хранилища (Сюй: RDBMS / Memcached / Redis / NoSQL). Следствия: отказ ноды = «вывели из ротации», без потери пользовательских сессий; автомасштабирование — добавляем/убираем ноды по нагрузке; деплой — катим по нодам без «приклеенных» пользователей.

Тонкость, которую стоит проговорить отдельно: stateless ≠ «в системе нет состояния». Стейт никуда не исчез — он переезжает в специализированные хранилища, которые масштабируются сами (репликацией и шардированием, разделы 05–06). Формула: «стейт не на вычислительных нодах — стейт в правильных местах». «Правильные места» — по классам хранилищ из ../knowledge-base.md §3:

Какой стейт Куда Почему
Сессии, фичи-флаги, счётчики KV (Redis-класс) O(1) по ключу, TTL, гигантский RPS на ноду
Данные домена, деньги, связи RDBMS ACID, источник правды
Горячие чтения Кэш-слой (read-реплики/KV-зеркало) Проекция под access pattern (раздел 06)
Медиа, бэкапы Объектное хранилище (S3) + CDN Бесконечный масштаб, дёшево (раздел 04)
События между сервисами Очередь/лог (Kafka) Асинхронная развязка (раздел 07)

5.3 Web-слой ≠ app-слой

Бриф зовёт этот сюжет «web-сервер vs app-сервер». Разделение: web-слой — терминирование соединений, TLS, раздача статики (reverse proxy); app-слой — бизнес-логика и воркеры. Платим раздельно, масштабируем раздельно: новый API → докинули app-нод, web не трогаем. Воркеры в app-слое — машина асинхронности: тяжёлые операции уходят из request-path в фон через очередь (шаг 9 лестницы). Service discovery (Consul/etcd/ZooKeeper) — ответ на «как сервисы находят друг друга», когда нод и сервисов много. Микросервисы — не цель: SDP прямо предупреждает про цену слабосвязанных сервисов (деплой, операции) — на секции взвешиваем; яндексовский KISS: «система должна быть минимально сложной».

6. Конечная схема: как это рисуется на этапе 3

Пять минут этапа 3 — блоки из этой статьи ложатся в каноническую схему (уровень «инстаграм/твиттер»):

  клиенты ──▶ DNS (GeoDNS: ведёт в ближайший регион)
                    │
                    ├──── статика ────▶ CDN ──▶ S3/origin
                    │                     (offload 90 %+)
                    └──── динамика ──▶ LB (L7, пара active-active,
                                        SSL termination, health checks)
                                          │
                                          ▼
                       stateless-сервисы (web/app, автомасштаб)
                         │            │            │
                         ▼            ▼            ▼
                       кэш       БД: primary    очередь ──▶ воркеры
                      (KV)       + реплики      (Kafka)    (транскодинг,
                                                             рассылки, …)

Проговариваем по ней: горячий путь целиком («клиент → GeoDNS в регион → CDN для статики / LB для динамики → любой stateless-сервис → кэш → при промахе реплика БД»), путь записи отдельно («сервис → primary → репликация»), и всё, что не нужно для ответа пользователю — асинхронно через очередь. Каждый блок на схеме — строка лестницы §2: LB (шаг 3), реплики (4), кэш (5), CDN (6), stateless (7), ДЦ (8), очередь (9).

7. Сводная таблица «сигнал из оценок → шаг масштабирования»

Мост к сигнальной таблице статьи 02 §8: расчёт называет проблему — этот раздел отвечает «какой блок добавляем»:

Сигнал из оценок Шаг масштабирования Раздел
QPS_пика > RPS_ноды LB + горизонтальный пул stateless-нод 03
CPU-bound из-за TLS на входе LB с SSL termination; считать ноды от handshake'ов 03
Egress ≥ Gbps, медиа-ответы CDN + объектное хранилище 03, 04
Read/write ≥ 100:1 Кэш-слой + read-реплики 06, 05
Сессии на нодах мешают деплою/масштабу Stateless + сессии в KV 03
Рост app-пула душит БД соединениями Пул соединений, кэш перед БД 03, 06
Пики ×5–10 к среднему Автомасштаб + очередь как буфер 07
Пользователи на нескольких континентах Мульти-ДЦ + GeoDNS 08
Storage ≥ десятки ТБ Шардирование / объектное хранилище 05, 04

8. Ошибки этого раздела

Подмножество топ-10 ошибок (../methodology.md §4), относящееся к масштабированию:

Ошибка Как выглядит Противоядие
«Сразу микросервисы на Kafka» Коробки без боли: очередь «на всякий случай» Каждый блок — от сигнала: боль → шаг (§2)
LB как SPOF Один балансировщик на всю схему Пара active-passive/active-active + health checks
Стики-сессии как план «LB приклеит пользователя к ноде» Stateless + сессии в KV; sticky — только как исключение с ценой
CDN «для всего» Редкие объекты в CDN, нет fallback при отказе края Hot-контент, TTL-баланс, origin-fallback (§4.3)
Stateless на словах «Серверы без состояния», но локальный кэш/файлы как стейт Аудит: что потеряется при гибели ноды — ничего
Вертикальный скейл как стратегия «Докупим памяти master'у» на 10× рост Тактика на старте; стратегия — горизонталь (§1.2)
Забытые соединения вниз по стеку 100 app-нод × 50 коннектов = БД лежит Пул соединений, проговаривать при росте пула (§3.5)
Компоненты без чисел «CDN снизит нагрузку» — на сколько? Offload 90 %+, RPS на ноду, ноды от дефицитного ресурса

9. Что назвать на секции (чек-лист раздела)

Обязательные фразы и действия, по которым видно, что раздел освоен. Полный чек-лист всех этапов — ../methodology.md §7; канон — ../knowledge-base.md §3 (классы хранилищ — куда выносить какой стейт) и §13.1–13.2 (DNS, алгоритмы LB).

На этапе 3 (схема): - Нарисовать канон §6 и проговорить горячий путь и путь записи отдельно. - «Web-слой stateless — сессии в Redis, любой сервер берёт любой запрос» — одной фразой при первом же пуле серверов. - «Клиенты видят только публичный IP LB, серверы общаются по внутренним» — безопасность края.

На этапе 4 (детали): - Алгоритм LB под нагрузку: round robin для stateless; least connections для долгих соединений; weighted при разной мощности — плюс «health checks выводят нездоровые ноды». - «L7, SSL termination на LB — бэкенды не тратят CPU на TLS» + помнить, что TLS часто и есть дефицитный ресурс ноды (~20k RPS, эталон §3.6). - Куда какой стейт — по таблице §5.2 (KB §3): сессии → KV, домен → RDBMS, медиа → S3 + CDN, события → очередь. - CDN: «push hot / pull rare / hybrid», TTL-баланс, инвалидация версионированием URL, fallback на origin при отказе края. - При росте пула: «соединения вниз по стеку — пул, иначе задушим БД».

На этапе 6 (эксплуатация): - Отказ LB → пара active-passive/active-active; отказ ноды → stateless, вывели из ротации, сессии живут. - Отказ CDN → клиенты на origin (деградация латентности, не доступности). - Отказ ДЦ → GeoDNS переливает трафик; синхронизация данных между ДЦ — асинхронная (мост в раздел 08).

Сквозные формулы раздела: - «Боль → шаг»: каждый компонент схемы обязан назвать свою боль. - «Горизонтальный масштаб = stateless + LB» — центральная связка. - «Порядок шагов зависит от дефицитного ресурса» — медиа-сервис начинает с CDN, не с LB.

10. Самопроверка «умею, если»

Критерий раздела из брифа: могу объяснить, что и в каком порядке добавляю при росте нагрузки и почему. Проверяется прогоном: закрыть материалы, взять произвольный сервис (доставка кофе, электронная библиотека, парковки) и вслух пройти лестницу §2 от одного сервера до мульти-ДЦ — каждый шаг как «сигнал роста → добавляю X → получаю Y → плачу Z». Затем сверка: каноническая схема §6 воспроизводится от руки за 5 минут с горячим путём и путём записи; таблица «какой стейт куда» (§5.2) — без подглядывания; эталонные числа §3.6 (20k RPS на воркер из-за TLS, 43 сервера на 500k RPS) — от руки. Протокол тренировок и рубрика 0–3 — ../methodology.md §6; сверка с полными разборами — ../classic-designs.md §1. Если лестница рассказывается без пауз, а на «зачем здесь LB?» рука тянется не к определению, а к боли и числу — раздел готов.

Связанные документы

  • 00-overview.md — обзорная статья по всем 12 разделам брифа (TASK-45.40)
  • 01-interview-framework.md · 02-estimations.md — соседние статьи: каркас, на этапе 3 которого рисуется схема, и сигнальная таблица §8, из которой приходят шаги этого раздела
  • ../brief.md — бриф: раздел 03 с критерием «умею, если»
  • ../knowledge-base.md — §3 классы хранилищ (куда выносить стейт), §13.1 DNS, §13.2 алгоритмы балансировщика, §13.8 CDN push/pull и метрики края
  • ../methodology.md — §2 карточки этапов 3–4, §4 топ-10 ошибок, §6 протокол тренировок, §7 одностраничная карточка на секцию
  • ../classic-designs.md §1 — эталон URL shortener: IPVS ×4 ДЦ, ко-локация KV, 43 сервера на 500k RPS
  • ../materials/alex-xu-vol1/ch-01-scaling-from-zero.md · ../materials/sdp-guide.md — источники: лестница эволюции и полный текст главы · сеть (DNS/CDN/L4-L7/reverse proxy) и прикладной слой
  • Соседние статьи: 04-data-models-storage.md (куда ложится стейт) · 05-replication-sharding.md (масштаб хранилища) · 06-caching.md (кэш-слой детально) · 07-queues-streams.md (асинхронность) · 08-fault-tolerance.md (мульти-ДЦ и отказы)