Проблема: сотни соединений к PostgreSQL
При разработке высоконагруженных веб-приложений на PostgreSQL одна из самых частых проблем — утечка соединений. Каждый дополнительный процесс PostgreSQL требует до 10 МБ памяти, что при 500 подключениях даёт 5 ГБ оверхеда. Типичная картина: 100 инстансов Django/Laravel, каждый держит 5 соединений — итого 500. База задыхается, а админ платит за лишнюю память. Мы более 7 лет настраиваем PostgreSQL-инфраструктуру для проектов с нагрузкой до 10 000 RPS. PgBouncer решает эту проблему, мультиплексируя клиентские соединения в небольшой пул реальных соединений к PostgreSQL. Экономия памяти достигает 25 раз: с 5 ГБ до 200 МБ.
Как работает PgBouncer и какой режим выбрать?
PgBouncer выступает прокси-слоем между приложением и базой данных. Клиентские соединения (их могут быть тысячи) подключаются к PgBouncer, а он передаёт запросы через пул постоянных соединений к PostgreSQL. Выбор режима пулинга критичен:
| Режим | Описание | Когда использовать | Ограничения |
|---|---|---|---|
| Session pooling | Соединение PG занято всё время сессии | Старые приложения с короткими сессиями | Экономия минимальна при длинных сессиях |
| Transaction pooling | Соединение возвращается после транзакции | Рекомендуется для веб-приложений | Несовместим с prepared statements до версии 1.21, SET вне транзакции, advisory locks |
| Statement pooling | После каждого запроса | Почти никогда не нужен | Сильно ограничивает SQL |
Для современных фреймворков мы используем transaction pooling. Он обеспечивает максимальную утилизацию соединений: после каждой транзакции канал возвращается в пул и готов обслужить другой запрос. Такая схема в 5 раз эффективнее прямых подключений при одинаковой пропускной способности.
Почему transaction pooling оптимален для веб-приложений?
Веб-приложения работают в парадигме «запрос-ответ»: каждый обработчик выполняет одну-две короткие транзакции. Transaction pooling позволяет 100 клиентским подключениям делить 20 реальных соединений к PG. Потери производительности нет, а выигрыш в памяти — в 5 раз. Единственное «но» — prepared statements на протокольном уровне. До версии PgBouncer 1.21 они не работали в transaction mode. Решения: отключить их в драйвере (например, prepare_threshold=None в SQLAlchemy) или обновить PgBouncer.
Что входит в настройку PgBouncer под ключ?
Внедрение PgBouncer включает полный цикл работ:
- Установку и конфигурацию pgbouncer.ini под вашу нагрузку.
- Настройку аутентификации (SCRAM-SHA-256 или auth_query).
- Адаптацию драйвера приложения (отключение prepared statements, если нужно).
- Развёртывание мониторинга через Prometheus + Grafana.
- Документацию по архитектуре и час гарантийной поддержки.
- Обучение команды работе с мониторингом.
Процесс работы
- Аналитика — снимаем метрики соединений, изучаем текущую конфигурацию.
- Проектирование — рассчитываем pool_size, max_client_conn, выбираем топологию (single/HA).
- Реализация — разворачиваем PgBouncer, правим код приложения.
- Тест — нагрузочное тестирование с помощью pgbench или k6.
- Деплой — подключаем мониторинг, настраиваем алерты.
- Пост-релиз — наблюдаем 48 часов, корректируем пул при необходимости.
Пример конфигурации pgbouncer.ini
[databases] mydb = host=127.0.0.1 port=5432 dbname=mydb user=app password=secret [pgbouncer] listen_addr = 0.0.0.0 listen_port = 6432 auth_type = scram-sha-256 auth_file = /etc/pgbouncer/userlist.txt pool_mode = transaction max_client_conn = 1000 default_pool_size = 20 min_pool_size = 5 reserve_pool_size = 5 reserve_pool_timeout = 3 server_connect_timeout = 15 server_login_retry = 15 query_timeout = 0 query_wait_timeout = 120 client_idle_timeout = 0 server_lifetime = 3600 server_idle_timeout = 600 log_connections = 0 log_disconnections = 0 log_pooler_errors = 1 stats_period = 60 admin_users = pgbouncer_admin stats_users = pgbouncer_stats Мониторинг и алерты
Подключаемся к псевдо-базе pgbouncer: SHOW POOLS; — ключевой показатель cl_waiting. Если он постоянно >0, увеличиваем default_pool_size. Для Prometheus используем pgbouncer-exporter — он отдаёт метрики: активные соединения, время ожидания, количество ошибок. Графана визуализирует тренды.
Как выбрать размер пула?
Размер пула зависит от числа ядер CPU и характера запросов. Эмпирическое правило: default_pool_size = 2 * (число ядер) + 1 для IO-bound задач. Для CPU-bound задач — не более числа ядер. Пример: на сервере с 8 ядрами и IO-bound нагрузкой ставим 17 соединений в пуле. Если запросы тяжёлые (аналитические), уменьшаем до 8. Точное значение подбирается нагрузочным тестированием.
Сравнение конфигураций для разных нагрузок
| Тип нагрузки | default_pool_size | max_client_conn | Рекомендация |
|---|---|---|---|
| Низкая (до 100 RPS) | 10 | 200 | Один PgBouncer на сервере приложения |
| Средняя (до 1000 RPS) | 20 | 500 | Выделенный сервер с PgBouncer |
| Высокая (более 1000 RPS) | 50 | 1000 | Кластер PgBouncer с HAProxy |
Сроки и гарантии
Установка и настройка PgBouncer для существующего приложения занимает от полдня до 1 дня. Свяжитесь с нами — оценим ваш проект за час. Закажите настройку под ключ и получите бесплатную консультацию по архитектуре. Мы сертифицированные инженеры PostgreSQL с коммерческим опытом. Даём гарантию на корректную работу пула в течение 30 дней после внедрения.







