Ошибка PostgreSQL «Too Many Connections»: решение с помощью PgBouncer
Автор: Javohir Abdullayev · · Сервер и DevOps

Введение: неожиданное появление проблемы
Каждый backend-разработчик или DevOps-инженер хотя бы раз в жизни сталкивается с этой пугающей ошибкой: FATAL: sorry, too many clients already. Недавно в одном из наших проектов среднего масштаба в сфере электронной коммерции произошла именно такая ситуация. После запуска маркетинговой акции посещаемость сайта резко возросла. В результате Django API перестал отвечать на запросы, а пользователи начали видеть ошибки 500 и 502.
Проверив мониторинг сервера, мы увидели, что ни процессор (CPU), ни оперативная память (RAM) не были перегружены. Проблема крылась в базе данных: PostgreSQL исчерпал лимит разрешенных соединений (connections) и перестал принимать новые запросы.
Почему соединения в PostgreSQL быстро заканчиваются?
Архитектура PostgreSQL устроена так, что под каждое новое клиентское подключение выделяется отдельный процесс. Каждый такой процесс потребляет ощутимый объем оперативной памяти сервера (обычно от 5–10 МБ и более). По умолчанию в PostgreSQL установлено значение max_connections = 100.
Наш Django-проект работал под управлением Gunicorn. 8 воркеров, каждый с несколькими потоками, плюс фоновые воркеры Celery одновременно устанавливали соединения с базой данных. С ростом числа пользователей каждый воркер Gunicorn удерживал свое соединение открытым. В итоге лимит в 100 подключений был исчерпан всего за пару минут.
Ошибочное решение: увеличение max_connections
Первой мыслью было самое простое решение: открыть postgresql.conf и увеличить max_connections до 500 или 1000. Однако это грубая ошибка. Управление сотнями активных процессов в PostgreSQL приводит к резкому замедлению из-за переключения контекста (context switching) в операционной системе, что в конечном счете может привести к падению (крэшу) всего сервера.
Настоящее решение: пул соединений PgBouncer
Чтобы решить эту проблему профессионально, мы решили установить PgBouncer. PgBouncer — это легковесный и чрезвычайно быстрый менеджер пула соединений (connection pooler) для PostgreSQL. Он работает как промежуточное звено между приложением и базой данных.
PgBouncer поддерживает 3 режима пулинга:
- Session pooling: соединение выделяется клиенту на все время сессии и удерживается до ее завершения.
- Transaction pooling: соединение возвращается в пул сразу после завершения транзакции. Это наиболее эффективный и популярный режим.
- Statement pooling: соединение освобождается после выполнения каждого отдельного SQL-запроса (не поддерживает транзакции).
Для нашего Django-приложения мы выбрали режим Transaction pooling. Благодаря этому 500 веб-запросов могут последовательно и без сбоев обрабатываться всего через 20–30 активных соединений с PostgreSQL.
Установка и настройка PgBouncer
Установка PgBouncer на сервер Ubuntu выполняется очень просто:
sudo apt update
sudo apt install pgbouncer -yПосле установки редактируем конфигурационный файл /etc/pgbouncer/pgbouncer.ini:
[databases]
my_database = host=127.0.0.1 port=5432 dbname=my_database
[pgbouncer]
logfile = /var/log/postgresql/pgbouncer.log
pidfile = /var/run/postgresql/pgbouncer.pid
listen_addr = 127.0.0.1
listen_port = 6432
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 25
reserve_pool_size = 5Добавляем учетные данные пользователей в файл /etc/pgbouncer/userlist.txt:
"db_user" "md5_parol_hesh_qiymati"Запускаем и проверяем службу PgBouncer:
sudo systemctl enable pgbouncer
sudo systemctl restart pgbouncerПодключение Django-проекта к PgBouncer
В настройках Django-проекта (settings.py) меняем порт базы данных со стандартного 5432 на порт PgBouncer — 6432:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'my_database',
'USER': 'db_user',
'PASSWORD': 'secret_password',
'HOST': '127.0.0.1',
'PORT': '6432',
}
}Важное примечание: при использовании режима Transaction pooling параметр Django CONN_MAX_AGE необходимо установить в 0, иначе могут возникнуть непредвиденные ошибки сессий.
Результаты и выводы
После внедрения PgBouncer мы провели повторное нагрузочное тестирование (load testing). Результаты превзошли ожидания:
- Даже при одновременных 800 параллельных запросах PostgreSQL стабильно работал всего с 25 соединениями.
- Ошибка
FATAL: sorry, too many clients alreadyполностью исчезла. - Потребление оперативной памяти сервера снизилось на 40%.
- Время ответа API (latency) стабильно удерживалось в диапазоне 45–60 мс.
Если ваш проект на Python или Django сталкивается с замедлением из-за нагрузки на базу данных, вместо слепого увеличения лимитов в настройках PostgreSQL рекомендуем настроить PgBouncer. Это надежное, экономичное и высокоэффективное решение.
Теги: #PostgreSQL #PgBouncer #Django #DevOps #Backend