PgBouncer в продакшне: режимы пулинга, лимиты соединений и переключение без простоя | AdminWiki

PgBouncer в продакшне: режимы пулинга, лимиты соединений и переключение без простоя

11 сентября 2026 6 мин. чтения
Содержание статьи

Зачем PgBouncer в продакшне и что он реально решает

PgBouncer - легковесный пулер соединений, который ставится перед PostgreSQL. PostgreSQL создает отдельный процесс на каждое клиентское соединение. При 500+ соединениях это приводит к высокому потреблению памяти (около 10 МБ на процесс) и переключениям контекста CPU. PgBouncer мультиплексирует тысячи клиентских соединений в десятки серверных.

В продакшне это решает три задачи: снижает нагрузку на СУБД, стабилизирует число соединений, ускоряет установку соединения (клиент подключается к PgBouncer, а не к PostgreSQL). Без PgBouncer при пиковой нагрузке вы упираетесь в max_connections, получаете ошибки "too many clients already" и деградацию.

Эта статья - практическое руководство. Мы разберем режимы пулинга, расчет лимитов, аутентификацию, TLS, мониторинг и переход без простоя. Неправильный режим пулинга ломает prepared statements и транзакционную логику. Начните с тестового окружения. Если вы только разворачиваете PostgreSQL, начните с руководства по установке и начальной настройке.

Когда PgBouncer обязателен, а когда можно обойтись

PgBouncer обязателен в сценариях: serverless-функции (Lambda, Cloud Functions) с короткоживущими соединениями; PHP-FPM с сотнями воркеров; микросервисы с большим числом инстансов; высокая конкуренция кратких транзакций. В этих случаях число клиентских соединений растет быстрее, чем PostgreSQL может обработать.

Можно обойтись без PgBouncer, если у вас единичные долгоживущие соединения (например, один аналитический сервер), низкая конкуренция (до 50 клиентов), или вы используете встроенный пулинг в драйвере (HikariCP, c3p0) с правильно рассчитанным размером пула. Если вы только проектируете базу, посмотрите, как решения на этапе проектирования влияют на администрирование: связь этапов жизненного цикла.

Режимы пулинга PgBouncer: session, transaction, statement

Три режима пулинга отличаются моментом возврата серверного соединения в пул. Session: соединение закреплено за клиентом до disconnect. Transaction: серверное соединение возвращается в пул после завершения транзакции. Statement: после каждого запроса. Сравнение в таблице.

РежимСовместимостьЭффективностьКогда использовать
sessionПолнаяНизкаяМиграции, тяжелые сессии, temp tables, cursors, legacy
transactionВысокая, но с ограничениямиМаксимальнаяВеб-приложения, ORM, микросервисы
statementНизкая (ломает транзакции)Теоретически высокаяПрактически не применяется

По умолчанию выбирайте transaction. Session - только при явной необходимости.

Transaction pooling: почему это дефолт для веб-приложений

Пример: 1000 клиентов при 20 серверных соединениях. PgBouncer принимает 1000 подключений, но держит только 20 процессов PostgreSQL. Большинство ORM (Django, SQLAlchemy, ActiveRecord) работают с transaction pooling при правильной настройке. Экономия: вместо 1000 процессов PostgreSQL получает 20. Пример из практики: интернет-магазин с 2000 запросов в секунду, каждый запрос в отдельной транзакции. PgBouncer пропускает их через 50 соединений. PostgreSQL не испытывает перегрузки.

Ограничения transaction pooling и как их обойти

Prepared statements: в transaction режиме серверное соединение меняется между транзакциями, поэтому prepared statements, созданные на одном соединении, недоступны на другом. Решения:

  • Использовать protocol-level prepared statements в PgBouncer 1.21+ (параметр max_prepared_statements).
  • Отключить server-side prepare в драйвере: для JDBC prepareThreshold=0, для psycopg2 убрать автоматическую подготовку.
  • Применять DEALLOCATE ALL перед возвратом соединения (не рекомендуется из-за накладных расходов).

Advisory locks не работают, так как сессия меняется. Выносите их в отдельное соединение или используйте таблицу. LISTEN/NOTIFY не работает, нужно отдельное соединение. SET search_path: используйте ALTER ROLE ... SET search_path, чтобы применялось ко всем соединениям роли. Временные таблицы с ON COMMIT DROP могут не работать, так как соединение возвращается в пул.

Session pooling: когда без него не обойтись

Session pooling нужен для миграций (Alembic, Flyway), которые требуют постоянного соединения для блокировок. Тяжелые аналитические сессии с курсорами. Приложения с session-level state: temp tables, advisory locks, LISTEN/NOTIFY. Legacy-код, который нельзя переписать. В этих случаях используйте session pooling, но помните о низкой эффективности. Можно комбинировать: для миграций настроить отдельный пул с session mode, а для веб-трафика - transaction.

Расчет pool_size и max_client_conn под вашу нагрузку

Формулы и практические ориентиры. default_pool_size ≈ (число CPU ядер на PostgreSQL) * 2-4, но не более max_connections минус резерв для суперпользователя и репликации. max_client_conn - исходя из пикового числа одновременных клиентов, обычно 2-5x от pool_size. max_db_connections - жесткий лимит на БД.

Формула и пример расчета для типового сервера

Пример: PostgreSQL 8 vCPU, max_connections=200, 3 приложения (веб, аналитика, фоновые задачи). default_pool_size=32, max_client_conn=1000, reserve_pool_size=8, reserve_pool_timeout=3.

[pgbouncer]
pool_mode = transaction
default_pool_size = 32
max_client_conn = 1000
reserve_pool_size = 8
reserve_pool_timeout = 3
max_db_connections = 150

Предупреждение: pool_size больше, чем PostgreSQL может обработать, дает очередь и деградацию. Не ставьте default_pool_size равным max_connections.

Таймауты: query_wait_timeout, client_idle_timeout, server_idle_timeout

query_wait_timeout=30s - отсекает запросы, ждущие соединение дольше 30 секунд. client_idle_timeout=600s - закрывает простаивающих клиентов. server_idle_timeout=60s - освобождает серверные соединения. Эти параметры защищают пул от зависших клиентов и соединений.

query_wait_timeout = 30
client_idle_timeout = 600
server_idle_timeout = 60

Аутентификация и TLS между клиентом, PgBouncer и PostgreSQL

auth_type: scram-sha-256 (рекомендуется), md5 (legacy), cert, trust (только локально). auth_file с хешами паролей или auth_query для динамической проверки.

auth_query: динамическая аутентификация без дублирования паролей

Создайте роль pgbouncer с доступом к pg_shadow. Настройте auth_query = SELECT usename, passwd FROM pg_shadow WHERE usename=$1. В pgbouncer.ini:

auth_type = scram-sha-256
auth_query = SELECT usename, passwd FROM pg_shadow WHERE usename=$1

PgBouncer должен иметь доступ к паролям в том же формате. Если PostgreSQL использует scram-sha-256, PgBouncer тоже должен быть собран с поддержкой scram.

Настройка TLS на клиентской и серверной стороне

Параметры client_tls_sslmode=require, client_tls_key_file, client_tls_cert_file. server_tls_sslmode=verify-full для проверки сертификата PostgreSQL. Требования к сертификатам: клиентский сертификат и ключ, корневой сертификат для проверки сервера.

client_tls_sslmode = require
client_tls_key_file = /etc/pgbouncer/client.key
client_tls_cert_file = /etc/pgbouncer/client.crt
server_tls_sslmode = verify-full
server_tls_ca_file = /etc/pgbouncer/root.crt

Проверка через openssl s_client -connect pgbouncer-host:6432 -starttls postgres.

Мониторинг, health checks и метрики PgBouncer

Admin-консоль: SHOW POOLS, SHOW STATS, SHOW CLIENTS, SHOW SERVERS, SHOW DATABASES. Ключевые метрики: cl_waiting, sv_active, sv_idle, avg_query_time. Экспорт в Prometheus через pgbouncer_exporter.

Ключевые метрики и пороги для алертов

cl_waiting > 0 в течение 5 минут - пул исчерпан. avg_wait_time > 100ms - деградация. sv_active == pool_size стабильно - нужно увеличивать пул или оптимизировать запросы. Также следите за errors, timeouts.

Health check для балансировщика нагрузки

Варианты: TCP-чек, admin-консоль SHOW VERSION, проверка через psql с коротким таймаутом. Health check не должен открывать новые серверные соединения. Если используете Nginx как балансировщик, настройте health check соответствующим образом: Nginx как балансировщик нагрузки.

Внедрение PgBouncer без простоя и план отката

Пошаговая стратегия. Если вы разворачиваете PgBouncer на облачном сервере, можно использовать Timeweb Cloud для гибкого масштабирования: Timeweb Cloud.

Канареечное внедрение: один инстанс приложения

Выделите один под/инстанс, направьте на PgBouncer, сравните latency и error rate с контрольной группой в течение 24-48 часов.

Управление PgBouncer без рестарта: PAUSE, RESUME, RELOAD

RELOAD - перечитать конфиг без разрыва. PAUSE - дождаться завершения текущих транзакций и остановить прием новых. RESUME - возобновить. KILL db - принудительно закрыть соединения к БД. Используйте для изменения конфигурации без рестарта.

План отката и типовые ошибки при внедрении

Откат: переключите DNS/HAProxy обратно на прямое подключение, PgBouncer не выключайте. Типовые ошибки: неверный pool_mode, забытый prepareThreshold=0, слишком маленький pool_size, отсутствие мониторинга cl_waiting. Перед запуском убедитесь, что у вас есть рабочий план восстановления, например, с помощью pgBackRest.

Чек-лист перед запуском PgBouncer в продакшн

  • Выбран pool_mode (обычно transaction).
  • Рассчитаны pool_size и max_client_conn.
  • Настроены таймауты: query_wait_timeout, client_idle_timeout, server_idle_timeout.
  • Включен TLS (client_tls_sslmode, server_tls_sslmode).
  • Настроена аутентификация (auth_type, auth_query или auth_file).
  • Работает мониторинг: SHOW POOLS, SHOW STATS, pgbouncer_exporter.
  • Есть план отката.
  • Протестированы prepared statements.
  • Проверены advisory locks и LISTEN/NOTIFY.

Команды для проверки конфигурации: SHOW CONFIG; SHOW POOLS; SHOW STATS; SHOW CLIENTS; SHOW SERVERS.

Поделиться:
Сохранить гайд? В закладки браузера