Что ломается первым под нагрузкой: быстрый фикс или архитектура
Кэш, увеличение таймаутов и новые реплики часто убирают симптом, но переносят отказ на следующий слой. Разбираем, как выбрать между быстрым исправлением и переделкой на примере API с PostgreSQL и Redis.

Когда API под нагрузкой начинает отвечать 502, база упирается в CPU, а очередь фоновых задач растёт, у команды обычно есть два пути: быстро добавить кэш или таймаут, либо менять границы сервиса и схему обработки запросов. Первый вариант сокращает время до восстановления, второй устраняет причину, но требует миграции и новых рисков. Ниже — критерии для системы с PostgreSQL 16, Redis 7 и веб-приложением, где сначала нужно понять, какой слой сломается следующим.
Сначала найдите не медленный запрос, а первый отказавший ресурс
Под нагрузкой система редко падает целиком в один момент. Обычно один ресурс исчерпывает запас, после чего начинает давить на соседние:
- пул соединений к PostgreSQL заканчивается, и запросы ждут свободное соединение;
- очередь запросов в приложении растёт, воркеры дольше удерживают соединения;
- таймауты на прокси обрывают запросы, хотя операция в базе продолжает выполняться;
- кэш начинает вытеснять полезные записи или становится единственной точкой отказа;
- фоновые задачи догоняют входящий поток и накапливают задержку.
Поэтому график средней задержки недостаточен. Смотрите на перцентили, число активных и ожидающих соединений к базе, время выполнения самых частых запросов, долю ошибок по маршрутам, длину очереди и частоту промахов кэша. Среднее значение может оставаться нормальным, пока часть пользователей уже получает таймауты.
Перцентиль1 показывает границу, ниже которой находится заданная доля измерений. Например, p95 — задержка, которую не превышают 95% запросов; остальные 5% могут ждать значительно дольше.
Три быстрых решения и цена каждого
Быстрый фикс не плох сам по себе. Проблема начинается, когда его принимают за архитектурное решение и перестают измерять последствия.
| Подход | Что выигрываете сразу | Что может сломаться следующим | Когда допустим |
|---|---|---|---|
| Кэширование результата | Меньше чтений из PostgreSQL и ниже задержка для повторяющихся запросов | Устаревшие данные, stampede при истечении ключа, давление на Redis | Данные допускают короткую задержку обновления, а запросы повторяются |
| Увеличение таймаутов | Меньше ошибок на коротком пике | Воркеры и соединения заняты дольше, очередь растёт быстрее | Операция действительно завершается чуть дольше, а не зависает из-за блокировки |
| Увеличение числа воркеров | Больше параллельных запросов на уровне приложения | PostgreSQL получает больше конкурирующих запросов, растёт расход памяти | CPU приложения свободен, а база и пул соединений имеют запас |
| Очередь для тяжёлой операции | Пользовательский запрос освобождается быстро | Повторная постановка задачи, задержка результата, рост очереди | Операцию можно выполнить асинхронно и безопасно повторить |
Самый опасный быстрый фикс — тот, который увеличивает параллелизм без проверки следующего слоя. Дополнительные воркеры не ускорят запрос, который ждёт блокировку в PostgreSQL. Они лишь создадут больше ожидающих клиентов.
Если исправление уменьшает задержку, но увеличивает очередь, число активных соединений или долю повторных попыток, это не устранение проблемы, а перенос точки отказа.
Как выбрать между кэшем, очередью и переделкой запроса
Кэш — когда чтение повторяется, а свежесть измерима
Кэш подходит для каталогов, публичных страниц, настроек и других данных, где допустимо заранее оговорённое устаревание. Перед внедрением зафиксируйте два параметра: допустимый возраст значения и действие после изменения исходной записи.
Проверяйте не только hit rate. Высокая доля попаданий не доказывает пользу: крупные значения могут занимать память, а сериализация ответа — съедать выигрыш. Отдельно измеряйте размер ответа, время чтения из Redis, количество промахов и число запросов к PostgreSQL после истечения ключей.
Если тысячи запросов одновременно видят истёкший ключ и все идут в базу, возникает cache stampede — лавина запросов после промаха кэша. Для такого случая нужны ограничение одновременного пересчёта, предварительный прогрев или разнесение сроков истечения. Конкретный механизм зависит от клиента Redis и библиотеки приложения, поэтому проверяйте его документацию для своей версии.
Очередь — когда результат не нужен в HTTP-ответе
Отправка письма, генерация отчёта, пересчёт рекомендаций и импорт файлов обычно не должны удерживать пользовательский запрос. Но перенос операции в очередь не делает её надёжной автоматически.
- Сделайте задачу идемпотентной: повторный запуск не должен дважды списать деньги или создать дубликат.
- Храните состояние операции в базе, чтобы пользователь видел «принято», «выполняется» или «ошибка», а не только HTTP 202.
- Задайте предел повторных попыток и отдельный маршрут для безнадёжно ошибочных задач.
- Следите за возрастом самой старой задачи, а не только за длиной очереди.
Очередь ломается первой, когда производительность потребителей ниже входящего потока. Если средняя скорость обработки не превышает скорость поступления задач, любой пик превращается в постоянный backlog. Масштабировать потребителей можно только после проверки ограничений базы, внешних API и лимитов памяти.
Индекс и переписывание запроса — когда база делает лишнюю работу
Если один и тот же запрос читает больше строк, чем возвращает, кэш маскирует проблему до первого изменения данных или промаха ключа. Сначала изучите план выполнения и фактическое число обработанных строк на репрезентативном объёме данных. В PostgreSQL для этого используют средства анализа плана из официальной документации, но результат нужно снимать на тестовой копии или в безопасное окно: анализ тяжёлого запроса сам создаёт нагрузку.
Индекс не бесплатен. Он занимает место, замедляет записи и может не помочь, если условие плохо селективно или типы сравнения заставляют приводить значение. После добавления индекса проверяйте не только целевой SELECT, но и INSERT, UPDATE, размер базы и время обслуживания.
Что измерить до решения
Без исходной точки команда спорит о впечатлениях. Минимальный набор измерений собирайте в течение периода, где есть обычная нагрузка и пик:
- Разложите задержку. Отдельно снимите время в прокси, приложении, PostgreSQL, Redis и внешних сервисах. Проверка: сумма компонентов примерно объясняет полное время запроса, а не скрывает его в одном общем таймере.
- Найдите хвост. Сравните p50, p95 и p99 по каждому критичному маршруту. Проверка: видно, растёт ли задержка у всех или только у небольшого процента запросов.
- Проверьте насыщение. Зафиксируйте CPU, память, дисковый ввод-вывод, число соединений к PostgreSQL, ожидание блокировок и размер очередей. Проверка: у первого ресурса, достигшего лимита, есть временная связь с ростом ошибок.
- Проверьте восстановление. Снимите, сколько времени система возвращается к норме после снижения нагрузки. Проверка: очередь и активные соединения уменьшаются, а не остаются на прежнем уровне.
- Повторите сценарий. Нагрузочный тест должен использовать близкие к боевым объём данных, профиль запросов и параллельность. Проверка: результат можно сравнить с исходной точкой по тем же метрикам.
Если таких замеров нет, нельзя честно утверждать, что «правильная» переделка быстрее. В этом случае сначала добавьте измерения и ограниченный тест, а не меняйте сразу несколько слоёв.
Что ломается первым в типичных сценариях
Увеличили таймаут, а 502 исчезли
Проверьте, не выросли ли число занятых воркеров и время удержания соединения к базе. Исчезновение 502 может означать, что прокси теперь терпит дольше, но пользователи ждут больше, а очередь запросов становится длиннее.
Добавили кэш, а данные стали противоречивыми
Ищите несколько путей изменения одной сущности: запись в базу может пройти, а удаление ключа — нет. Для критичных данных определите, что считается допустимой задержкой, и не кэшируйте состояние, которое нельзя показывать устаревшим.
Добавили воркеры, а база начала отказывать
Сравните число процессов приложения с лимитом соединений и фактическим временем ожидания. Пул соединений должен соответствовать пропускной способности базы, а не просто числу CPU приложения.
Перенесли операцию в очередь, но результат создаётся дважды
Причиной обычно становится повторная доставка или таймаут подтверждения. Добавьте ключ идемпотентности и уникальное ограничение там, где дубликат недопустим. Повторная попытка должна быть штатным сценарием, а не исключением.
Когда быстрый фикс превращается в долг
Оставляйте временное решение только с владельцем, сроком пересмотра и метрикой удаления. Запись «потом переписать» не работает, если никто не знает, какой сигнал должен запустить переделку.
Для каждого фикса зафиксируйте:
- какой симптом он снимает;
- какой ресурс начинает потреблять больше;
- при каком значении метрики его нужно отключить или пересмотреть;
- какие данные могут стать устаревшими или дублироваться;
- как откатить изменение без ручного редактирования продакшен-базы.
Если проблема повторяется при каждом пике, меняется профиль запросов или растёт объём данных, временный кэш и новые таймауты уже не отвечают на вопрос о пропускной способности. Нужны нагрузочный тест, ревизия запросов и отдельное проектирование отказоустойчивости.
Что делать дальше
Начните с одного критичного маршрута и одной измеримой гипотезы: например, выясните, что даст больше — индекс в PostgreSQL 16, кэш ответа в Redis 7 или перевод тяжёлой операции в очередь. Снимите p95, p99, число активных соединений, ошибки и время восстановления до изменения. После этого внесите только один фикс, повторите тот же сценарий и проверьте, не переместился ли отказ в следующий слой.
Инвалидация кэша и выбор конкретного брокера сообщений — отдельные темы; здесь важнее критерий выбора и порядок измерений.
1 Перцентиль — статистическая граница распределения задержек, а не среднее значение. Чем выше перцентиль, тем ближе он к худшему опыту пользователей.
Локализатор видео: Перевод и озвучка видео на другой язык с укладкой в исходный тайминг. Глоссарий терминов, отметки проблемных реплик.
Спросить за 250 ₽Источники: PostgreSQL 16 Documentation: Monitoring Database Activity, Redis Documentation
Частые вопросы
Почему нельзя просто увеличить таймауты?
Когда кэш действительно лучше переписывания запроса?
Как понять, что пора переносить операцию в очередь?
Что делать, если после добавления воркеров база стала работать хуже?
Какая метрика показывает, что быстрый фикс пора убирать?
Нужен ли нагрузочный тест, если проблема уже воспроизводится в проде?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.