Как устроено интервью, какие вопросы задать в начале, как за пять минут прикинуть нагрузку и почему важны не средние, а хвосты. Первая часть серии.
Листайте стрелками ← →, свайпом или кнопками внизу. В схемах нажимайте кнопки и двигайте ползунки.
Правильного ответа нет. Смотрят на ход мысли: умеете ли вы сузить расплывчатую задачу, прикинуть масштаб, собрать работающую схему, углубиться в самое сложное место и честно назвать компромиссы.
Для уровня senior и выше главный сигнал — вы ведёте разговор сами, а не ждёте вопросов. Нажмите на этап или двигайте ползунок времени.
Пример — сокращатель ссылок. Включайте требования и смотрите, какие компоненты и решения из них следуют.
Задача: «Спроектируйте ленту новостей, как в Twitter». У вас есть время на один уточняющий вопрос. Какой сильнее всего изменит архитектуру?
| Время и объёмы | |
|---|---|
| сутки | 86 400 с ≈ 10⁵ с |
| год | ≈ 3·10⁷ с |
| 1 млн событий в сутки | ≈ 12 в секунду |
| 1 млрд в сутки | ≈ 12 тыс. в секунду |
| 2¹⁰ / 2²⁰ / 2³⁰ / 2⁴⁰ | ≈ тысяча / миллион / миллиард / триллион — КБ, МБ, ГБ, ТБ |
| UUID, timestamp, int64 | 16 / 8 / 8 байт |
| строка в таблице | 100 Б – 1 КБ с индексами |
| пик к среднему | обычно ×2–×3, распродажи и события — ×10 |
| Что держит один узел (порядок величины) | |
|---|---|
| сервис на Go/Java, 16 ядер | 10–50 тыс. rps на простом эндпоинте, 1–5 тыс. с логикой и походами в базу |
| PostgreSQL | чтения по индексу — десятки тыс./с, запись — 5–20 тыс. транзакций/с, комфортно до единиц ТБ |
| Redis | ~100 тыс. операций/с на ядро, ответ < 1 мс, объём — сколько RAM |
| Kafka | брокер — сотни МБ/с; партиция — единицы–десятки МБ/с |
| сервер | 32–128 ядер, 256 ГБ – 1 ТБ RAM, сеть 10–25 Гбит/с (1–3 ГБ/с) |
| NVMe SSD / HDD | сотни тысяч IOPS и 3–7 ГБ/с / 100–200 IOPS |
Это ориентиры, а не обещания: реальная цифра зависит от запросов, размера данных и железа. На интервью важна не точность, а порядок: 100 или 100 000 записей в секунду — разные системы. Как устроены эти технологии — в презентации про серверный стек.
Цель — понять, какой класс системы нужен: хватит ли одной базы с репликами, нужен ли кэш, шардирование, CDN. Считайте круглыми числами и сразу говорите вывод.
Выберите пример или двигайте ползунки. Ёмкость узлов — со слайда с числами.
Разница между уровнями — порядки. Поход в другой дата-центр стоит как сто тысяч обращений к памяти. Отсюда правила: держите горячие данные в памяти, не ходите в сеть в цикле, не пересекайте океан на каждом запросе.
Как устроена иерархия памяти внутри процессора — в презентации про процессор. Цифры типичны для серверов в 2020-х.
Полоса — логарифмическая шкала: каждый шаг вправо — примерно ×10.
Доступность — доля времени (или запросов), когда система отвечает правильно. Каждая девятка уменьшает допустимый простой в 10 раз — и обычно в разы увеличивает стоимость.
Двигайте ползунок. Обратите внимание, где кончаются ручные действия: при четырёх девятках дежурный не успевает даже открыть ноутбук.
Добавляйте копии и делайте зависимости мягкими. Формула верна только для независимых отказов: копии в одной зоне, с одним конфигом и одним релизом падают вместе.
Сервис получает 1 000 запросов в секунду. Выберите SLO и устраивайте инциденты.
p99 = 120 мс значит: 99% запросов быстрее 120 мс, а каждый сотый — медленнее. Среднее прячет хвост: редкие паузы GC почти не двигают его, но именно их видит пользователь.
Медленные запросы часто достаются самым ценным клиентам: у них больше данных. Добавьте паузы GC и промахи кэша и посмотрите, что сдвинется.
Если страница ждёт N параллельных вызовов, она медленная, когда медленный хоть один: P = 1 − (1 − p)ⁿ. Редкий для одного сервиса хвост становится нормой для пользователя.
Лечение: меньше вызовов на запрос, таймауты с частичным ответом, хеджированные запросы — копия запроса на другую реплику, если первая не ответила за p95 (подробнее — в «The Tail at Scale», Dean и Barroso). Нажимайте «Запрос» или прогоните тысячу.
Страница собирается из 100 параллельных вызовов к разным сервисам. У каждого p99 = 50 мс, вызовы независимы. Какая доля открытий страницы займёт больше 50 мс?
Среднее число запросов в системе = пропускная способность × время ответа: L = λ·W. Верно для любой устойчивой системы — пула соединений, воркеров, очереди.
Двигайте ползунки. Попробуйте замедлить запрос к базе с 5 до 50 мс — так выглядит инцидент «база стала медленнее, и упал весь сервис».
Запросы приходят неравномерно. Даже если сервер в среднем успевает, в моменты скопления образуется очередь. Для простейшей модели (M/M/1) среднее время ответа T = S / (1 − ρ), где S — время обработки, ρ — загрузка.
50% загрузки — ответ вдвое дольше обработки, 90% — в 10 раз, 99% — в 100. Поэтому мощности планируют с запасом и держат загрузку на 50–70%. Двигайте ползунок загрузки и смотрите на очередь.
Сервис загружен на 80%. Обработка запроса — 10 мс, среднее время ответа с очередью — 50 мс. Трафик вырос на 10%, загрузка стала 88%. Каким станет среднее время ответа?
Хорошая схема: потоки данных подписаны стрелками, запись и чтение различимы, у хранилищ указан ключ. Нажимайте «Дальше».
?after=<id>&limit=50. OFFSET 100000 заставляет базу прочитать и выбросить 100 тысяч строк, а при вставках страницы съезжают.Сеть может потерять ответ, и клиент не знает, выполнилась ли операция. Повторять обязательно — значит повтор должен быть безопасным. Включайте и выключайте ключ и отправляйте платёж.
Выбор базы следует из запросов, а не наоборот. Выпишите сущности и главные шаблоны доступа: по какому ключу читаем, что сортируем, что пишется чаще всего. Ключ определит и индексы, и будущий ключ шардирования.
(chat_id, id), шард по chat_id: весь чат на одном узле.Про выбор хранилища, шардирование и репликацию — в третьей части серии.
-- шаблон доступа: последние сообщения чата
create table messages (
chat_id bigint not null,
id bigint not null, -- растёт со временем (Snowflake)
author_id bigint not null,
body text not null,
created_at timestamptz not null default now(),
primary key (chat_id, id)
);
-- страница истории — курсором по ключу
select id, author_id, body from messages
where chat_id = $1 and id < $2
order by id desc
limit 50;
# p99 по всем экземплярам сервиса (Prometheus)
histogram_quantile(0.99,
sum by (le) (rate(http_request_duration_seconds_bucket[5m])))
# доля ошибок за час против бюджета SLO 99,9%
sum(rate(http_requests_total{code=~"5.."}[1h]))
/ sum(rate(http_requests_total[1h])) / 0.001
# нагрузочный тест: 2000 rps, смотрим p99
$ vegeta attack -rate=2000 -duration=60s < targets | vegeta report
Второе выражение — скорость сжигания: 1 значит, что бюджета хватит ровно на 30 дней, 14,4 за час — что месячный бюджет кончится за двое суток. На такое будят дежурного.
Каждое решение — компромисс. Назовите его вслух.