Масштабирование и производительность: кэширование, очереди, шардирование, rate limiting
После того как вы:
собрали требования и SLO
набросали высокоуровневую архитектуру и контракты
определились с данными, транзакциями и консистентностьюна System Design интервью неизбежно возникает вопрос: выдержит ли система нагрузку и что произойдёт при пиках, деградациях и злоупотреблениях.
Для Engineering Manager этот блок особенно важен, потому что он показывает управленческую зрелость:
вы не «оптимизируете в вакууме», а улучшаете конкретные SLI
вы контролируете риск инцидентов и стоимость эксплуатации
вы умеете выбирать достаточные меры под ограничения команды!Общая карта, где в архитектуре обычно применяются кэш, очереди, шардирование и лимиты
Откуда берутся «цифры»: RPS, пики и узкие места
На интервью не ждут идеальных расчётов, но ждут умения оценивать порядок величин и связывать его с решениями.
Базовая формула, которую можно назвать вслух:
-
Где:
— количество запросов
— время в секундах
— среднее число запросов в секундуПрактически важнее не формула, а вопрос: какой у нас пик и что считается критичным путём по SLO.
Типовые причины «внезапных» пиков:
сезонность и маркетинговые кампании
релизы мобильного клиента
повторные запросы из-за таймаутов и ретраев
проблемы зависимости, из-за которых растёт очередь запросовКэширование
Кэш — это инструмент для снижения задержек и нагрузки на более дорогие компоненты (БД, внешние сервисы). В интервью важно проговорить три вещи:
что именно кэшируем
где кэш живёт
как управляем устареванием и инвалидациейБазовая документация: Документация Redis.
Где кэш ставят в архитектуре
Клиентский кэш
- Например, HTTP caching, локальное хранение на мобильном клиенте.
- Снижает нагрузку, но сложнее контролировать актуальность.
Edge-кэш
- CDN для статики и иногда для публичных API.
- Хорошо для контента и редко меняющихся ответов.
Серверный кэш рядом с сервисом
- Чаще всего Redis.
- Даёт лучший контроль и наблюдаемость.
Кэш внутри процесса
- Быстро, но риск несогласованности при нескольких инстансах.
Что кэшировать: типовые цели
Read-heavy данные с терпимой задержкой актуализации
Результаты дорогих вычислений
Справочники и конфигурации
Рендеренные шаблоны (например, для уведомлений)Отсылка к предыдущей статье про данные: источник истины обычно остаётся в OLTP-хранилище, а кэш — это ускоритель чтений, который может быть eventually consistent.
Стратегии кэширования
Cache-aside (lazy loading)
- Приложение сначала смотрит в кэш, при промахе читает из БД и кладёт в кэш.
- Плюсы: просто внедрять.
- Минусы: нужен контроль инвалидации и «шторм» при массовых промахах.
Read-through
- Кэш сам ходит в БД при промахе.
- Плюсы: выносит логику заполнения.
- Минусы: сложнее, чаще платформенное решение.
Write-through
- Сначала пишем в кэш, кэш синхронно пишет в БД.
- Плюсы: кэш всегда тёплый.
- Минусы: кэш становится частью критичного пути по доступности.
Write-behind (write-back)
- Пишем в кэш, в БД — асинхронно.
- Плюсы: быстрые записи.
- Минусы: риск потери данных и сложности консистентности; на интервью это нужно отдельно обосновывать.
Инвалидация и TTL: самый частый «крючок» интервью
Кэширование редко «сложно» само по себе. Сложно — поддерживать предсказуемую актуальность.
Типовые подходы:
TTL (время жизни ключа)
- Простая модель: данные могут быть устаревшими максимум на TTL.
- Хорошо для справочников и нестрогих данных.
Явная инвалидация по событию
- При изменении данных публикуется событие, по нему удаляется/обновляется кэш.
- Хорошо, когда нужна более свежая выдача.
Версионирование ключей
- Ключ включает версию сущности, при обновлении меняется версия.
- Полезно, когда явную инвалидацию делать сложно.
EM-сигнал: назвать риск «thundering herd» — когда истёк TTL у популярного ключа, и много инстансов одновременно идут в БД. Типовые меры:
добавлять «джиттер» к TTL
использовать блокировку на заполнение (single flight)
делать stale-while-revalidate: отдаём слегка устаревшее и обновляем в фонеОчереди и асинхронная обработка
Очередь — это инструмент, который:
сглаживает пики (буферизует нагрузку)
изолирует критичный путь от медленных зависимостей
упрощает ретраи и повышает надёжность обработкиВажно: очередь не «ускоряет» обработку сама по себе. Она делает систему более управляемой при перегрузке.
Реальные источники:
Документация Apache Kafka
Документация RabbitMQОчередь как часть SLO
Связь с темой SLO из прошлых статей:
API на критичном пути может отвечать быстро с семантикой accepted
реальная работа переносится в асинхронный контур
SLO на обработку формулируется как «доля задач, обработанных за N секунд»Гарантии доставки и их последствия
At-most-once
- Дубликатов почти нет, но возможна потеря.
- Подходит для некритичных событий.
At-least-once
- Потеря минимизируется, но дубликаты возможны.
- Требует идемпотентных обработчиков и/или дедупликации.
Отсылка к статье про данные и консистентность: at-least-once почти всегда приводит к разговору про идемпотентность, ключи дедупликации и дизайн состояния.
Ретраи, backoff и dead-letter очередь
Зрелый ответ на интервью включает политику ошибок:
Retry с экспоненциальной задержкой
- Снижает давление на зависимость.
Circuit breaker и таймауты
- Не даём очереди раздуваться бесконечно из-за зависшего провайдера.
Dead-letter queue (DLQ)
- Сообщения, которые не удалось обработать после лимита попыток.
- Нужен процесс разбора: алерт, ручная обработка, автопочинка.
EM-сигнал: проговорить ownership DLQ — кто несёт ответственность и какой SLA у разбора.
Backpressure: что делаем при перегрузке
Если вход превышает пропускную способность воркеров, очередь растёт. Вам нужен план:
лимиты на входе (rate limiting)
динамическое масштабирование воркеров
приоритизация (критичное отдельно от некритичного)
деградация: что сбрасываем/задерживаем первымШардирование и работа с «горячими» данными
Шардирование — разбиение данных по нескольким узлам, чтобы масштабировать объём и запись. Часто его путают с репликацией:
репликация обычно помогает чтениям и отказоустойчивости
шардирование — про масштаб записи и общий объём!Почему выбор shard key определяет равномерность нагрузки и риск «горячих» шардов
Когда шардирование действительно нужно
Типовые сигналы:
запись упёрлась в один инстанс БД, вертикально расти некуда
объём данных приближается к эксплуатационному пределу (бэкапы, вакуум, время восстановления)
есть горячие разделы данных, которые нельзя разгрузить репликамиEM-сигнал: шардирование — дорогая мера по сложности. На интервью хорошо звучит позиция: сначала репликация/партиционирование/оптимизация запросов, потом шардирование.
Выбор shard key
Правило: выбирайте ключ, который:
присутствует почти во всех запросах
обеспечивает равномерное распределение
минимизирует кросс-шардовые операцииТиповая ловушка: «красивый» ключ, который создаёт перекос.
Примеры:
сообщения в чате: шардирование по chat_id часто лучше, чем по user_id
заказы: иногда по user_id, иногда по order_id, в зависимости от запросов
события: часто по (tenant_id, time_bucket)Ребалансировка и миграции шардов
Шардирование почти всегда приводит к вопросу: что будет, когда шардов станет больше.
Практичные варианты, которые можно проговорить:
Consistent hashing
- Уменьшает объём перемещаемых данных при добавлении узлов.
- Чаще встречается в распределённых KV/кэшах.
Диапазонное шардирование
- Прозрачно для запросов, но сложнее ребалансировать.
Каталог маршрутизации (shard map)
- Отдельный слой/таблица, которая говорит, где живёт ключ.
- Требует высокой надёжности этого каталога.
Если не уверены в деталях, на интервью достаточно назвать главный риск: миграции без даунтайма и контроль двойной записи/чтения на время переезда.
Партиционирование по времени как «дешёвый выигрыш»
Отсылка к статье про данные: если данные естественно временные (события, логи, статусы), партиционирование по времени часто даёт большой эффект:
быстрее удаление старых данных
проще TTL и управление объёмом
меньше индексный мусорИсточник: Документация PostgreSQL про партиционирование.
Rate limiting и защита системы
Rate limiting — это не про «безопасность ради безопасности». Это про управляемость SLO и предотвращение самоуничтожения системы при пиках и ошибках клиентов.
В интервью важно назвать:
где ставим лимиты (edge, gateway, сервис)
по какому ключу лимитируем (user, token, IP, tenant, client_id)
что делаем при превышении (ошибка, деградация, постановка в очередь)Базовые справочные источники:
Token bucket
Leaky bucketТиповые политики лимитов
Пер-пользователь
- Защищает от злоупотреблений.
Пер-клиент или пер-интеграцию
- Особенно важно для внутренних сервисов и партнёров.
Пер-кампанию или пер-тенант
- Защищает от «одного большого клиента», который съедает всё.
Глобальный лимит на компонент
- Предохранитель для защиты БД/провайдера.
Что возвращаем клиенту
Хороший контракт обычно включает:
понятный код ошибки (например, HTTP 429)
рекомендации по ретраю (например, через заголовок Retry-After)
корреляционный идентификатор для расследованияEM-сигнал: договориться о том, какие клиенты имеют приоритет (например, критичный внутренний контур) и как это оформлено организационно.
Как выбрать технику: кэш, очередь, шардирование, лимиты
Ниже — упрощённая матрица, которую удобно держать в голове на интервью.
| Проблема | Часто помогает | Что выигрываем | Что усложняем | Типичный вопрос интервьюера |
|---|---|---|---|---|
| Высокая задержка чтения, дорогие запросы | Кэш | p95/p99 latency, нагрузка на БД | Инвалидация, консистентность | Что будет с устаревшими данными? |
| Пики нагрузки и медленные зависимости | Очередь | Сглаживание пиков, изоляция критичного пути | Ретраи, DLQ, наблюдаемость очередей | Как гарантируете обработку без дублей? |
| Запись/объём упёрлись в одну БД | Шардирование/партиционирование | Масштаб объёма и записи | Миграции, кросс-шардовые запросы | Что с транзакциями и джойнами? |
| Перегрузка, злоупотребления, каскадные отказы | Rate limiting | Защита SLO и зависимостей | Политики, fairness, исключения | Кого лимитируем и почему? |
Мини-кейс: сервис уведомлений под пики
Свяжем всё с примером из предыдущих статей.
Требования:
критичные уведомления должны доходить быстро
маркетинговые можно задерживать
провайдеры имеют ограничения по RPS
нельзя дублировать отправки при ретраяхВысокоуровневое решение:
Rate limiting на входе
- лимит по
client_id и типу уведомлений
- защита от «взрыва» маркетинга
Две очереди или приоритеты
- критичная очередь обрабатывается отдельным пулом воркеров
- маркетинговая деградирует первой
Идемпотентность
- на приёме через
Idempotency-Key
- у воркеров — дедупликация по
notification_id
Кэширование шаблонов
- шаблоны и справочники каналов кэшируются с TTL
Рост данных
- статусы уведомлений партиционируются по времени
- архивирование старых записей
Сильное завершение на интервью: перечислить 2–3 ключевых риска и как вы их проверите:
нагрузочное тестирование на пики
хаос-тесты на недоступность провайдера
алерты на рост очередей и долю DLQЧто обычно проверяют у EM в этом блоке
умение выбрать достаточную технику под SLO и ограничения
понимание, что кэш и очередь меняют консистентность и требуют контрактов
зрелость в эксплуатации: метрики, алерты, DLQ, деградация
понимание стоимости: шардирование и микросервисы повышают нагрузку на on-callЧто дальше по курсу
Теперь у вас есть набор практических рычагов, которые превращают «скелет архитектуры» в систему, выдерживающую рост и пики.
Следующий логичный шаг в подготовке к интервью — связать всё в единый рассказ про надёжность и эксплуатацию: наблюдаемость, SLI/SLO на компоненты, стратегии деградации и работа с инцидентами.