База легла под нагрузкой: как разобрать инцидент
Приложение отвечает таймаутами, подключения к PostgreSQL копятся, а перезапуск лишь ненадолго возвращает сайт к жизни. Разбираем типичный сценарий инцидента: что проверить сначала, как отличить нехватку соединений от блокировок и что измерять после восстановления.

Приложение начинает отдавать таймауты, новые подключения к PostgreSQL не проходят, а после перезапуска сервиса проблема возвращается. Это типичный сценарий перегрузки, а не описание конкретного инцидента. Разберём его на примере PostgreSQL 16: как найти узкое место, не спутать следствие с причиной и проверить, помогли ли изменения.
Сначала зафиксируйте, что именно отказало
«База легла» может означать разные вещи: сервер PostgreSQL недоступен, запросы стали выполняться медленно или приложение не может получить соединение из пула. Эти сбои требуют разных действий. Увеличение лимита подключений не ускорит тяжёлый запрос, а перезапуск базы не устранит блокировку, которую приложение создаёт снова.
До перезапуска, если это безопасно, сохраните временной интервал инцидента, сообщения приложения и метрики базы. Перезапуск обрывает текущие запросы и очищает часть важного контекста. Если сервис уже недоступен, сначала восстановите его по своему аварийному плану, а доступные журналы и мониторинг сохраните отдельно.
- Проверьте подключения. Сколько соединений занято и какова их доля от настроенного предела? Сравните число соединений в PostgreSQL с числом активных запросов.
- Посмотрите ожидания. Запросы ждут блокировку, диск, процессор или ответ клиента? Само наличие множества подключений ещё не объясняет причину.
- Сопоставьте графики по времени. Нужны нагрузка CPU, дисковая задержка и пропускная способность, свободное место, количество соединений, ошибки приложения и длительность запросов.
- Сверьте журналы. Ищите ошибки подключения, отмены запросов, сообщения о нехватке ресурсов и отметки времени, совпадающие с пиком.
Если метрики снимает только приложение, добавьте наблюдение со стороны самой базы и операционной системы. Иначе легко принять очередь в пуле за проблему PostgreSQL или, наоборот, пропустить упор в диск.
Найдите очередь, а не просто большую цифру
В PostgreSQL 16 состояние подключений видно в pg_stat_activity. Запрос ниже показывает активные запросы, их ожидания и продолжительность. Запускайте его во время сбоя или сразу после: поздний снимок может показать уже другую картину.
SELECT pid,
application_name,
state,
wait_event_type,
wait_event,
now() - query_start AS query_duration,
left(query, 200) AS query_sample
FROM pg_stat_activity
WHERE datname = current_database()
AND state = 'active'
ORDER BY query_start;
Большое число активных запросов — повод выяснить, чем заняты серверные процессы. Ожидание блокировки указывает на конкуренцию за данные; ожидание ввода-вывода — возможную задержку на диске. Но одно поле не ставит диагноз: проверяйте повторяющиеся снимки, системные метрики и журналы вместе. Если запрос показывает не все сведения о чужих сессиях, проверьте права мониторинга.
Чтобы найти сессии, которые блокируют другие, выполните отдельный запрос:
SELECT blocked.pid AS blocked_pid,
blocker.pid AS blocker_pid,
blocked.wait_event_type,
blocked.wait_event,
now() - blocked.query_start AS blocked_duration,
left(blocked.query, 200) AS blocked_query,
left(blocker.query, 200) AS blocker_query
FROM pg_stat_activity AS blocked
JOIN LATERAL unnest(pg_blocking_pids(blocked.pid)) AS b(pid) ON true
JOIN pg_stat_activity AS blocker ON blocker.pid = b.pid
WHERE blocked.datname = current_database();
Не завершайте сессии вслепую. Сначала выясните, что делает блокирующий запрос и кто его запустил. Принудительное завершение может отменить транзакцию, вызвать повторные запросы приложения и усилить пик. Действуйте по процедуре дежурной команды и учитывайте последствия для записи данных.
Четыре причины, которые часто путают
| Что видно | Возможная причина | Что проверить дальше |
|---|---|---|
| Почти все доступные подключения заняты, запросы ждут выдачи соединения | Слишком много соединений, медленные запросы или утечка соединений в приложении | Размеры пулов на всех экземплярах приложения, время удержания соединения, число активных и простаивающих сессий |
| Много запросов ждут блокировку | Длинная транзакция или конфликтующие операции записи | Блокирующую сессию, длительность транзакций, код, который удерживает транзакцию открытой |
| Запросы медленные, диск загружен или задержки выросли | Чтение или запись упираются в хранилище; возможны большие сканирования и избыток операций ввода-вывода | Дисковые метрики, планы тяжёлых запросов и динамику по одинаковым интервалам |
| CPU близок к насыщению, медленных запросов стало больше | Резкий рост нагрузки, неудачный план запроса или дорогое вычисление | Топ запросов по суммарному времени, частоту их вызовов и планы на репрезентативных данных |
Для статистики запросов часто используют расширение pg_stat_statements, но оно должно быть настроено заранее. После инцидента проверьте, собиралась ли статистика и не сброшена ли она. Без неё можно начать с логов длительных запросов, если их запись была включена, и с доступных снимков pg_stat_activity.
Почему увеличение лимита соединений может ухудшить ситуацию
В PostgreSQL сервер обслуживает клиентские подключения отдельными серверными процессами. Если приложение развернули на нескольких экземплярах и на каждом создали большой пул, суммарное число соединений может быстро вырасти. Дополнительные процессы расходуют память и ресурсы CPU, а активные запросы всё равно конкурируют за диск и данные. Поэтому поднять max_connections — не универсальное лечение.
Сначала оцените, сколько соединений реально используют приложения, и проверьте, закрывают ли они транзакции и возвращают ли соединения в пул. Если приложению нужно много коротких соединений, рассмотрите внешний пулер, например PgBouncer, но отдельно проверьте его режим работы, лимиты и совместимость с используемыми функциями PostgreSQL. Пулер уменьшает число прямых подключений к базе, но не делает тяжёлые запросы быстрее.
Если очередь растёт быстрее, чем база успевает обрабатывать запросы, добавление новых подключений увеличивает очередь, но не пропускную способность.
Термин — пул соединений: слой, который повторно использует уже открытые подключения к базе, вместо того чтобы каждый раз создавать новое. Настройки пула есть и в приложении, и в отдельных сервисах; считайте их суммарно по всем экземплярам.
Замеры до и после: что считать доказательством
Если у вас нет проверенного нагрузочного теста, не подставляйте в отчёт случайную цифру запросов в секунду. Зафиксируйте условия, при которых сравниваете систему: версию PostgreSQL, конфигурацию сервера, объём и характер данных, число экземпляров приложения, параллельность и набор запросов. Сравнение «до и после» без этих условий мало что говорит.
- Запишите долю занятых подключений и время ожидания соединения в пуле.
- Сравните задержки запросов по распределению, а не только среднее значение: отдельно смотрите быстрые и самые медленные обращения.
- Отследите ожидания PostgreSQL, загрузку CPU, задержки диска и свободное место на тех же временных интервалах.
- Проверьте частоту ошибок и таймаутов приложения, а также объём повторных попыток.
- Повторите тест при той же нагрузке после изменения и убедитесь, что улучшение не перенесло очередь в другой слой.
Причину перегрузки ищите в сочетании сигналов. Например, много соединений вместе с ожиданием блокировки — не то же самое, что много соединений при насыщенном CPU. Одиночный график или снимок не подтверждает диагноз.
Где диагностика ломается
Снимок сделан после восстановления. Очередь уже исчезла, и база выглядит здоровой. Настройте сбор метрик и журналов заранее, иначе после события останутся только косвенные признаки.
Активное ожидание принимают за поломку базы. Например, PostgreSQL может ждать данные от клиента. Проверьте тип и событие ожидания, состояние сессии и соотнесите их с метриками приложения.
Повторные попытки создают второй пик. Если приложение сразу повторяет каждый таймаут, нагрузка после первого сбоя растёт. Проверьте политику повторов и ограничьте её так, чтобы сбой одного запроса не запускал бесконечный поток новых.
Поднимают лимит без расчёта суммарного числа соединений. Учитывайте все копии приложения, фоновые задачи и административные подключения. Изменение лимита не должно подменять поиск медленных запросов, блокировок или слишком больших пулов.
Эти признаки не покрывают все причины: нехватка памяти, проблемы хранилища, ошибки сети и особенности конкретных запросов требуют отдельной проверки. Не меняйте одновременно несколько настроек — иначе будет трудно понять, что повлияло на результат.
Что делать дальше
Начните с наблюдаемости: сохраните графики соединений, ожиданий, CPU и диска, настройте сбор статистики запросов и проверьте журналы приложения. Затем воспроизведите характерную нагрузку на тестовой среде и изменяйте по одному фактору. После каждого изменения сравнивайте задержки, ошибки и размер очереди при одинаковых условиях.
Для PostgreSQL 16 сверяйте названия и смысл полей с официальной документацией именно этой версии. Отдельно изучите конфигурацию подключений и мониторинг: следующий полезный показатель — не только число запросов, но и время, которое запрос проводит в очереди до начала выполнения.
Сценарист праздников: Тосты, квесты, сценарии корпоративов и дней рождения.
Спросить за 8 ₽Источники: PostgreSQL 16: мониторинг статистики, PostgreSQL 16: настройки подключений
Частые вопросы
Стоит ли сразу увеличить max_connections?
Можно ли перезапустить PostgreSQL, чтобы быстро вернуть сервис?
Что делать, если pg_stat_activity не показывает текст запроса?
Поможет ли PgBouncer, если база медленно выполняет запросы?
Как понять, что проблема была именно в подключениях, а не в диске?
Что измерять после исправления?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.