Проектирование высоконагруженных систем: как посчитать нагрузку и что ломается первым
Разговор про highload обычно начинается после первого падения в пик: реклама сработала, пришло в десять раз больше людей, чем обычно, а сайт лёг на самом интересном месте. Мы в Code Pilots делаем веб-приложения и порталы с высокими нагрузками — от магазина на 500 тысяч позиций до приложения фестиваля с сотней тысяч одновременных пользователей, и почти всегда узкое место оказывается не там, где его ищут.
Если совсем коротко. Высокая нагрузка — не абстрактный статус, а конкретные цифры: пиковые запросы в секунду, объём данных и допустимое время ответа. Считать их надо до разработки, потому что архитектура зависит именно от них.
Первым ломается почти всегда база данных, а не «сервер». Отдельно стоит проверить контракт с клиентами: если API спроектирован без пагинации и лимитов, лишнюю нагрузку создаёт сам клиент. Помогают четыре вещи: убрать состояние из сервисов, добавить кеш, вынести тяжёлое в очереди и научить систему деградировать вместо падения. И до релиза всё это нужно проверить нагрузочным тестом — иначе вы узнаете правду в пик.
Что считается высокой нагрузкой
Единого порога нет, и любой, кто называет точное число, продаёт услугу. Практический ориентир по уровням:
| Уровень | Порядок нагрузки | Что обычно достаточно | Что уже нужно |
|---|---|---|---|
| Обычный | До сотни запросов в секунду, база в десятки гигабайт | Один сервер приложения, одна база, базовое кеширование | Нормальные индексы и мониторинг |
| Средний | Сотни запросов в секунду, заметные суточные пики | Несколько инстансов за балансировщиком, кеш, реплика для чтения | Stateless-сервисы, вынос тяжёлых операций в фон |
| Высокий | Тысячи запросов в секунду, терабайты данных | Горизонтальное масштабирование, кеш-кластер, очереди, разделение чтения и записи | Продуманная схема данных, лимиты, деградация |
| Экстремальный | Десятки тысяч запросов в секунду, резкие всплески | Шардирование, распределённые кеши, отдельные контуры под нагрузку | Постоянные нагрузочные прогоны, дежурство, план на отказ |
Важнее уровня — форма нагрузки. Ровные тысячи запросов в секунду проектировать проще, чем всплеск в сорок раз на пять минут: во втором случае система должна успеть подняться или мягко ограничить поток. Именно всплески дают самые дорогие падения — распродажи, эфиры, старт продаж билетов.
Полезно сразу развести нагрузку и объём. Миллион товаров в каталоге и миллион пользователей в минуту — разные задачи: первая про схему данных и поиск, вторая про горизонтальное масштабирование. Как устроена серверная часть в целом, разобрано в материале про бэкенд.
Как посчитать свою нагрузку до разработки
Считается это на салфетке, и без такого расчёта архитектуру обсуждать бессмысленно.
Шаг 1 — активные пользователи. Сколько людей пользуется сервисом в сутки. Для нового продукта берётся план по маркетингу, а не мечта.
Шаг 2 — действия на пользователя. Сколько экранов и запросов делает один человек за сессию. Отдельно считаются запросы к API от мобильного приложения: их обычно в разы больше, чем кажется.
Шаг 3 — коэффициент пика. Суточные запросы делятся не на 86 400 секунд, а на реальное окно активности. Для сервиса с вечерним пиком коэффициент к среднему — от трёх до десяти; для распродажи или эфира — десятки.
Шаг 4 — целевое время ответа. 200 миллисекунд на API-запрос и 2 секунды на страницу — разные бюджеты по архитектуре и деньгам.
Шаг 5 — данные. Объём базы через год, размер медиа, срок хранения истории. Часто именно рост данных, а не запросы, требует шардирования.
Итог расчёта — четыре числа: пиковые запросы в секунду, целевой отклик, объём данных на горизонте года и допустимое время недоступности. С ними уже можно спорить об архитектуре предметно, а не «давайте сделаем масштабируемо».
Что ломается первым
Порядок почти всегда один и тот же, и это не про мощность сервера.
База данных. Тяжёлые запросы без индексов, блокировки при массовых обновлениях, счётчики в одной строке, полнотекстовый поиск по таблице на десятки миллионов записей.
Запросы в цикле. Классический N+1: страница дёргает базу по разу на каждый элемент списка. На тестовых данных незаметно, на реальном каталоге — сотни запросов на одну страницу.
Медиа и файлы. Картинки без кеша и без внешней раздачи упираются в приложение; генерация превью на лету в пик добивает процессор.
Синхронные вызовы наружу. Оплата, доставка, склад, СМС: внешний сервис отвечает медленно, ваши потоки заняты ожиданием, очередь запросов растёт, всё стоит.
Состояние в памяти процесса. Сессии и корзины, лежащие в одном инстансе, не дают добавить второй сервер: пользователя перекидывает и он теряет данные.
Логи и метрики. Неожиданный, но частый пункт: синхронная запись подробных логов в пик тормозит само приложение.
Найти узкое место без данных нельзя, поэтому начинается всё не с переписывания, а с профилирования. В проектах, которые мы подхватываем, аудит кода и архитектуры обычно показывает, что до 80% просадки дают несколько запросов и отсутствие кеша — их правят за дни, а не за месяцы.
Паттерны, которые реально работают
| Паттерн | Что даёт | Когда вводить | Цена решения |
|---|---|---|---|
| Stateless-сервисы | Возможность добавить инстансы простым копированием | Сразу, это дешёво | Сессии и файлы выносятся во внешние хранилища |
| Кеш | Снимает основную часть однотипных чтений | При первых просадках на чтении | Появляется вопрос инвалидации |
| Реплики для чтения | Разгружают основную базу | Когда чтений в разы больше записей | Задержка репликации, часть данных «отстаёт» |
| Очереди | Быстрый ответ пользователю, тяжёлое в фоне | Как только появляются длинные операции | Асинхронность усложняет логику и отладку |
| Идемпотентность | Повтор запроса не создаёт второй заказ | Сразу для платежей и внешних обменов | Нужны ключи операций и хранение результатов |
| Разделение чтения и записи | Отдельные модели под разные нагрузки | Когда одна схема не устраивает и тех и других | Сложнее код, нужна синхронизация |
| Шардирование | Горизонтальный рост базы | Когда данные и запись не влезают в один узел | Самый дорогой шаг: меняется схема и логика |
| Автомасштабирование | Инстансы поднимаются под нагрузку | При заметных пиках | Холодный старт: успеть подняться до пика |
Порядок ввода важен не меньше самого списка: сверху вниз он идёт от дешёвого к дорогому. Ошибка «сразу шардируем» встречается чаще, чем ошибка «забыли про кеш».
База данных: где заканчивается «докупить сервер»
Вертикальное масштабирование — самое простое и до определённого предела самое разумное решение: больше памяти и быстрее диски дают кратный рост без переписывания кода. Предел наступает, когда упирается запись — её нельзя размножить репликами.
Дальше три шага, по возрастанию цены:
Реплики для чтения. Отчёты, каталог, поиск уходят на реплики, основная база остаётся для записи. Учтите задержку: только что созданный заказ может не появиться в отчёте мгновенно, и это нужно предусмотреть в интерфейсе.
Разделение по доменам. Логи, аналитика, поиск и очереди переносятся из основной базы в специализированные хранилища. Часто этого достаточно, чтобы отложить шардирование на годы.
Шардирование. Данные делятся на независимые части, у каждой свой мастер. Это меняет и схему, и код: появляется ключ шардирования, кросс-шардовые запросы становятся дорогими, а транзакции между шардами — отдельной задачей.
Практическое правило: слой абстракции над доступом к данным стоит заложить сразу, даже если шардирование не планируется. Тогда переход не потребует переписывания половины приложения. О том, как выбор технологий влияет на такие решения, — в материале про стек технологий.
Кеш: где он помогает и где стреляет в ногу
Кеш — самый быстрый способ снять нагрузку и самый быстрый способ получить новый класс проблем.
Что кешируют. Результаты тяжёлых запросов, карточки товаров, справочники, ответы внешних сервисов, готовые фрагменты страниц.
Главная проблема — инвалидация. Данные изменились, а в кеше лежит старая версия. Стратегия выбирается осознанно: срок жизни, сброс по событию, версионирование ключей. Универсального ответа нет, но «кешируем на час и надеемся» — не стратегия.
Холодный старт. После перезапуска или сброса кеш пуст, и вся нагрузка падает на базу — ровно в тот момент, когда она меньше всего этого ждёт. Лечится прогревом заранее.
Лавина запросов. Ключ истёк, и сто параллельных запросов одновременно бегут в базу за одним и тем же. Решается блокировкой на пересчёт или обновлением до истечения срока.
Что нельзя кешировать бездумно. Остатки товара, цены с персональными условиями, права доступа. Здесь ошибка кеша стоит дороже, чем экономия на запросах.
Очереди и асинхронность
Очередь — это способ ответить пользователю сразу, а тяжёлую работу сделать потом. Отчёты, письма, экспорт, обработка изображений, обмен с внешними системами, пересчёт рекомендаций — всё это не должно происходить в момент нажатия кнопки.
Что даёт очередь под нагрузкой: сглаживает пики, потому что задачи копятся и обрабатываются с постоянной скоростью; изолирует сбои, потому что упавший внешний сервис не роняет ваш интерфейс; позволяет масштабировать обработчики отдельно от веб-части.
Чем платите: усложняется логика (пользователь не видит результат сразу), нужны понятные статусы, обработка повторов и мёртвых сообщений, мониторинг длины очереди. Асинхронность — это не «поставили RabbitMQ», а проектирование поведения продукта.
Из практики: у B2B-дистрибьютора алкоголя мы держим обмен с хранилищем данных на RabbitMQ и Go с отликом меньше полусекунды, а у «Радиоэлементов» с 500 тысячами позиций конструктор парсеров прайс-листов обрабатывает файл поставщика за 30–120 секунд вместо часов ручной работы — это тоже фоновая обработка, просто в промышленном масштабе. Техническая сторона обменов между системами разобрана в материале про интеграционную шину.
Деградация вместо падения
Самая недооценённая часть проектирования. Любая система имеет предел, и вопрос не в том, наступит ли он, а в том, как система себя поведёт.
Лимиты на вход. Ограничение частоты запросов на пользователя и на общий поток. Лучше вежливо притормозить часть трафика, чем упасть для всех.
Отключение тяжёлых функций. В пик можно выключить персональные рекомендации, отчёты, экспорт, тяжёлые фильтры. Продукт беднеет на час, но работает.
Предохранитель на внешние вызовы. Если сервис доставки не отвечает, запросы к нему прекращаются на время, а пользователю показывается понятное сообщение вместо бесконечного ожидания.
Очередь ожидания. На старте продаж билетов честная очередь с номером и оценкой времени лучше, чем сайт, который не открывается.
Заранее подготовленные заглушки. Статическая версия каталога, кешированная главная, режим «только чтение». Их надо не придумывать в момент аварии, а проверить заранее.
Нагрузочное тестирование
Без теста все рассуждения об архитектуре — гипотезы. Нагрузочное тестирование отвечает на три вопроса: где предел, что ломается первым, как система восстанавливается.
Профиль нагрузки. Тест повторяет реальное поведение: доля чтений и записей, распределение по разделам, длина сессии. Ровный поток запросов на главную ничего не проверяет.
Метрики. Смотреть надо не среднее время ответа, а перцентили: p95 и p99. Среднее прячет ровно тех пользователей, из-за которых вы получите жалобы.
Виды прогонов. Постепенный рост до предела, длительный тест на стабильность, резкий всплеск, тест восстановления после отказа компонента.
Инструменты. В российских проектах чаще всего встречаются четыре:
| Инструмент | Чем хорош | Когда брать |
|---|---|---|
| Yandex.Tank | Заточен под высокие нагрузки, хорошо стреляет по HTTP, детальные отчёты | Когда нужен жёсткий обстрел и много запросов с одной машины |
| k6 | Сценарии на JavaScript, удобно встраивается в CI | Когда тесты пишет команда разработки и они живут вместе с кодом |
| JMeter | Зрелый инструмент с графическим интерфейсом и множеством протоколов | Когда тестируют не только HTTP и нужны готовые плагины |
| Gatling | Точные отчёты и хорошая работа с длительными сценариями | Когда важна детальная картина по перцентилям в динамике |
Выбор зависит не от рейтингов, а от того, что ваша команда сможет поддерживать: тест, который никто не запускает после релиза, бесполезен.
Где тестировать. На стенде, максимально похожем на прод по конфигурации, с реалистичным объёмом данных. Тест на пустой базе даёт красивые цифры и никакой информации.
Дальше начинается цикл: нашли предел, поправили узкое место, повторили. Первые два-три прохода обычно дают кратный рост без изменения архитектуры — за счёт индексов, запросов и кеша.
Наблюдаемость и дежурство
Система, за которой никто не смотрит, о своих проблемах сообщит через клиентов. Минимальный набор, который закладывается в проект, а не «потом»:
- Метрики: запросы в секунду, время ответа по перцентилям, ошибки, загрузка ресурсов, длина очередей, попадания в кеш.
- Логи с идентификатором запроса, чтобы проследить путь одной операции через все сервисы.
- Трассировка — где именно в цепочке потерялись секунды.
- Алерты по симптомам, а не по всему — растёт доля ошибок, деградирует p99, копится очередь. Алерт, который срабатывает каждый день, перестают читать.
- Дежурство и порядок эскалации: кто смотрит, что делает первым, кого поднимает.
- Цели по доступности (SLO) — договорённость о том, какой уровень сбоев считается нормой. Без неё спор «работает или нет» бесконечен.
У себя мы держим мониторинг обменов со статусами и алертами в Telegram: для интеграционных контуров это первый признак проблемы, который приходит раньше жалоб.
Когда highload вам не нужен
Обратная сторона темы, о которой подрядчики говорят редко. Преждевременное усложнение обходится дорого и замедляет разработку.
Признаки, что вам пока хватит простой архитектуры: нагрузка в пределах сотни запросов в секунду, база в десятки гигабайт, пики предсказуемые и не в разы, а на десятки процентов. В этой ситуации микросервисы, шардирование и кластер кеша дадут не устойчивость, а сложность в эксплуатации и счёт за инфраструктуру.
Что действительно стоит сделать сразу и стоит недорого: убрать состояние из сервисов, завести мониторинг, закрыть очевидные узкие места в запросах, спроектировать схему данных с учётом роста и заложить слой абстракции над доступом к данным. Этого достаточно, чтобы вырасти в десять раз без переписывания.
Мы говорим об этом прямо на первом звонке: если задача не требует highload-архитектуры, продавать её мы не будем.
Сколько стоит и из чего складывается
Стоимость highload-проекта складывается из четырёх частей: проектирование архитектуры и схемы данных, разработка, инфраструктура с запасом мощности и нагрузочное тестирование с последующей оптимизацией. Последние две части в первых сметах обычно отсутствуют, а потом дают неприятный сюрприз.
Отдельно считается запас: система, рассчитанная ровно на ожидаемый пик, ляжет на первом же превышении прогноза. Разумный подход — держать кратный запас по мощности на короткие всплески и уметь докупать ресурсы быстро.
Ориентиры по нашим работам: сложные и высоконагруженные системы начинаются от 5 млн ₽, аудит кода и архитектуры существующей системы — от 200 до 600 тыс. ₽, отдельная интеграция — от 150 тыс. до 1,5 млн ₽. Точная сумма зависит от профиля нагрузки, требований к отклику и состояния текущего кода: оценка проекта бесплатная — опишите задачу.
Сильный аргумент в защиту бюджета — стоимость простоя. Час недоступности в пик распродажи считается в упущенной выручке и в оттоке; на этом фоне запас по мощности и нагрузочный прогон выглядят дешёвой страховкой. Порядок суммы для такой защиты удобно прикинуть в бесплатном калькуляторе оценки.
Ошибки
Проектировать по среднему. Средняя нагрузка за сутки не говорит ничего: система живёт или умирает в пиковую минуту.
Начинать с микросервисов. Распределённая система на старте даёт распределённые проблемы: сетевые задержки, согласованность данных, отладка через пять сервисов.
Кешировать всё подряд. Без стратегии инвалидации кеш начинает отдавать неверные остатки и цены — и это дороже медленных запросов.
Забыть про идемпотентность. Повторный запрос из-за таймаута создаёт второй заказ и второй платёж; всплывает это в самый неудачный момент.
Не тестировать под нагрузкой. Единственный способ узнать предел — прогон. Все остальные способы называются надеждой.
Оставить систему без наблюдаемости. Без метрик и трассировки инцидент разбирают по описаниям пользователей, а не по данным.
Не предусмотреть деградацию. Система, которая умеет только работать полностью или не работать вовсе, обязательно выберет второе в неподходящий момент.
С чего начать
Три шага, которые дают предметный разговор. Первое — посчитать профиль нагрузки: пиковые запросы в секунду, целевое время ответа, объём данных на год вперёд, допустимое время недоступности. Второе — снять метрики с текущей системы, если она есть: время ответа по перцентилям, самые тяжёлые запросы, попадания в кеш, длину очередей. Третье — определить критичный сценарий, который нельзя терять ни при какой нагрузке: оформление заказа, оплата, регистрация на событие.
С этими данными видно, нужна ли вам новая архитектура или достаточно оптимизации существующей. По нашему опыту второе встречается чаще, и это хорошая новость для бюджета.
Часто задаваемые вопросы
Единого порога нет. Ориентир: до сотни запросов в секунду хватает одного сервера с кешем; сотни требуют нескольких инстансов за балансировщиком и реплики для чтения; тысячи — горизонтального масштабирования и очередей; десятки тысяч — шардирования. Важнее уровня форма нагрузки: всплеск в сорок раз на пять минут проектируется иначе, чем ровный поток.
Почти всегда база данных: тяжёлые запросы без индексов, блокировки, счётчики в одной строке. Дальше — запросы в цикле (N+1), медиа без внешней раздачи, синхронные вызовы внешних сервисов, сессии в памяти процесса и, неожиданно, подробное логирование. Искать узкое место нужно профилированием, а не переписыванием: часто несколько запросов и отсутствие кеша дают основную часть просадки.
Нет, это разные вещи. Монолит с stateless-сервисами, кешем и очередями держит очень высокую нагрузку. Микросервисы решают организационную задачу — независимые команды и релизы — и добавляют сетевые задержки, сложность отладки и проблемы согласованности данных. На старте они чаще вредят, чем помогают.
Нагрузочным тестом на стенде, похожем на прод, с реалистичным объёмом данных и профилем поведения пользователей. Смотреть надо p95 и p99, а не среднее время ответа. Прогоны нужны разные: постепенный рост до предела, длительный на стабильность, резкий всплеск и восстановление после отказа компонента. Плюс заранее проверенный режим деградации: что вы отключите, если поток превысит расчёт.
У нас сложные и высоконагруженные системы начинаются от 5 млн ₽, аудит кода и архитектуры — от 200 до 600 тыс. ₽, интеграция — от 150 тыс. до 1,5 млн ₽. Кроме разработки в смету входят проектирование схемы данных, инфраструктура с запасом и нагрузочное тестирование. Точная цифра — по профилю нагрузки, оценка бесплатная.
Разработка с Code Pilots
Code Pilots — студия заказной разработки полного цикла, и высокие нагрузки вместе со сложными интеграциями — наш профиль, а не побочная компетенция.
Для VK Fest мы собрали приложение фестиваля с сотней тысяч одновременных пользователей: MVP за два месяца командой из пяти человек и первое место в Google Play. «Радиоэлементы» с 500 тысячами позиций работают с нами больше десяти лет, PetShop — тоже: highload-магазин, а мобильное приложение на готовой API-платформе вышло за две недели.
Что мы делаем в таких проектах: считаем профиль нагрузки, проектируем схему данных и архитектуру, закрываем узкие места, строим обмены на очередях, настраиваем наблюдаемость и прогоняем нагрузочные тесты до релиза, а не после. DevOps внутри проекта у нас включён всегда.
Границы обозначим честно: серверный парк и железо мы не аудируем и не поставляем — работаем с кодом, архитектурой и инфраструктурой в облаке. Если задача не требует highload-решений, скажем об этом сразу. Обсудить проект →