pgBouncer

Для чего нужен пулер соединений
Количество соединений, которое поддерживает сервер БД PostgreSQL, ограничено. Поэтому нормальная логика работы клиентского приложения с БД предполагает, что приложение:
Устанавливает соединение с сервером БД.
Отправляет SQL-запрос и получает результат.
Закрывает соединение, освобождая его для следующих клиентов.
Проблема в том, что PostgreSQL выделяет отдельный процесс на каждое установленное соединение. Постоянное открытие и закрытие множества соединений означает запуск и остановку множества процессов, что создает большие накладные расходы ресурсов сервера и приводит к снижению производительности СУБД.
Чтобы решить эту проблему, в DBaaS Postgres для всех инстансов установлен менеджер соединений PgBouncer (пулер). Логика его работы следующая:
Пулер устанавливает с сервером БД фиксированное количество соединений (пул), которые держит открытыми все время. В DBaaS Postgres размер пула по умолчанию (default_pool_size pgbouncer) составляет 200 соединений.
Клиентское приложение подключается не напрямую к БД, а к пулеру. Максимальное количество поддерживаемых соединений между приложением и пулером составляет 6000.
Пулер коммутирует клиентское соединение на сервер, используя открытое соединение из пула.
Когда приложение закрывает соединение с пулером, соединение между пулером и сервером возвращается в пул и может быть переиспользовано следующим клиентским соединением.
Режимы работы PgBouncer
Менеджер соединений PgBouncer в DBaaS Postgres работает в двух режимах:
Сессионный — в этом режиме клиентское соединение устанавливается при первом запросе к базе данных и поддерживается до тех пор, пока клиент не разорвет сессию. Такой режим поддерживается всеми клиентами PostgreSQL, но является менее производительным, чем транзакционный режим. PgBouncer в сессионном режиме доступен на порту
5432.Транзакционный — в этом режиме клиентское соединение устанавливается при первом запросе к базе данных и поддерживается до завершения транзакции. Транзакционный режим обеспечивает высокую производительность и позволяет максимально эффективно нагрузить СУБД. PgBouncer в транзакционном режиме доступен на порту
6432.
Однако транзакционный режим поддерживается не всеми клиентами PostgreSQL, и в нем недоступно использование некоторых функций PostgreSQL:
временных таблиц (temporary tables);
курсоров (cursors);
рекомендательных блокировок (advisory locks), которые существуют дольше одной транзакции;
подготовленных операторов (prepared statements).
Experiment
Preparation
Having following docker-compose file:
This creates the test tables (pgbench_branches, pgbench_accounts, etc.) and populates them with sample data.
Without pgBouncer
-c 50: Use 10 concurrent clients.-j 4: Use 4 threads.
result may look like
With pgBouncer
-d maindb (this comes from pgbouncer.ini where I define an alias maindb which leads to actual postgres database in postgres container)
PgBouncer psql
Of course it is possible to connect to pgBouncer using psql client
where 6432 - port to connect to pgBouncer
maindb - db alias configured in pgbouncer.ini for particular backed database
pgBouncer does not load balance across replicas or clusters pgBouncer does not detect or switch to a standby automatically
Session vs Transaction modes
Scenario 1: pgBouncer in session Mode
pgBouncer in session Mode📌 Behavior
A single PostgreSQL session is assigned to a client connection for its entire lifetime.
All transactions from the same client use the same backend PID.
Session-scoped features (like temporary tables, GUCs,
SET LOCAL, etc.) persist across transactions.
✅ Use Cases
Applications that rely on:
Temporary tables
Session variables (
SET LOCAL app.user_id = '123')Listeners (
LISTEN/NOTIFY)Extensions like
pg_trgmwith session-local settings
🧠 Spring Boot + Hibernate Behavior
One
EntityManagermay reuse the same connection (session) across multiple@Transactionalmethods.Session state (e.g.,
SET LOCAL) persists across transactions.Temporary tables created in one transaction are visible in subsequent transactions.
⚠️ Drawbacks
Less scalable: Each client connection holds a backend connection.
Risk of connection leaks if clients don’t close connections.
🧪 Scenario 2: pgBouncer in transaction Mode
pgBouncer in transaction Mode📌 Behavior
A backend connection is assigned only for the duration of a single transaction.
After
COMMITorROLLBACK, the connection is returned to the pool and can be reused by another client.Session-scoped features do not persist across transactions.
✅ Use Cases
Stateless applications
High concurrency with many short transactions
Applications that don’t rely on session state
🧠 Spring Boot + Hibernate Behavior
Each
@Transactionalmethod may get a different backend PID.Session variables (
SET LOCAL) are lost between transactions.Temporary tables are not visible across transactions (since they’re session-scoped).
Hibernate and Spring can still work fine, as long as they don’t rely on session state.
⚠️ Drawbacks
Incompatible with:
Temporary tables
SET LOCALvariablesLISTEN/NOTIFYExtensions that rely on session state
Last updated
Was this helpful?