PgBouncer упирается в одно ядро — ClickHouse показал, как это обойти
1 июля 2026 года команда ClickHouse опубликовала разбор давней проблемы PgBouncer — самого популярного пулера соединений для PostgreSQL: он однопоточный. Неважно, сколько ядер на сервере — один процесс PgBouncer использует ровно одно из них, и именно пулер, а не сама Postgres, чаще всего оказывается узким местом под нагрузкой.
В чём проблема на практике
На 16-ядерном сервере пятнадцать ядер простаивают, пока одно тянет весь пул соединений. ClickHouse на реальном железе (AWS c7i.4xlarge, 16 vCPU) показал это наглядно: с ростом числа клиентов с 8 до 256 throughput единственного процесса PgBouncer сначала растёт до ~87 000 транзакций в секунду, а затем падает до ~77 000 — все клиенты конкурируют за одно и то же ядро, которое утилизируется на 97%, при этом сервер в целом загружен меньше чем на 10%.
Решение — флот процессов на SO_REUSEPORT
Вместо одного процесса ClickHouse запускает несколько процессов PgBouncer, привязанных к одному и тому же порту через SO_REUSEPORT — балансировку между ними берёт на себя ядро Linux, а клиенты по-прежнему подключаются к одному эндпоинту, не зная о флоте за ним. Это официально задокументированный способ задействовать больше одного ядра для PgBouncer — просто мало кто его настраивает вручную.
Ловушка: запрос на отмену запроса (cancel) в Postgres приходит по новому соединению с отдельным ключом — и ядро может отправить его на другой процесс флота, который вообще не знает об этом запросе. Отмена просто теряется. Решение — peering: процессы флота знают друг о друге и пересылают cancel-запрос тому процессу, который реально держит нужную сессию.
Бюджет соединений (max_client_conn, max_db_connections) при этом делится между процессами флота, чтобы суммарно не превысить лимиты самой Postgres.
Результат на бенчмарках
| Клиентов | 1 процесс, TPS | Флот из 16, TPS |
|---|---|---|
| 8 | 8 910 | 6 450 |
| 32 | 54 203 | 64 244 |
| 64 | 86 570 | 219 439 |
| 128 | 83 463 | 320 547 |
| 256 | 76 893 | 336 469 |
При малом числе клиентов один процесс даже немного быстрее — делить нечего, а соединения флота распределяются тонким слоем. Разрыв открывается ровно там, где это важно — под реальной конкурентной нагрузкой, где один процесс упирается в потолок одного ядра, а флот продолжает расти почти в 4 раза быстрее.
Как повторить это на своём self-hosted сервере
В отличие от managed-решения ClickHouse, у тебя на VPS это придётся собрать руками — но принцип тот же самый.
1. Несколько инстансов PgBouncer с SO_REUSEPORT
Начиная с PgBouncer 1.19+ поддержка SO_REUSEPORT встроена. В pgbouncer.ini каждого процесса:
listen_addr = 0.0.0.0
listen_port = 6432
so_reuseport = 1
Все процессы слушают один и тот же порт — ядро само распределяет входящие соединения.
2. Peering между процессами
Для корректной обработки cancel-запросов процессы должны знать друг о друге — актуальные версии PgBouncer поддерживают конфигурацию peers:
[peers]
1 = host=127.0.0.1 port=6433
2 = host=127.0.0.1 port=6434
3. Запуск флота через systemd template unit
# /etc/systemd/system/pgbouncer@.service
[Unit]
Description=PgBouncer instance %i
After=network.target
[Service]
User=postgres
ExecStart=/usr/bin/pgbouncer /etc/pgbouncer/pgbouncer-%i.ini
Restart=on-failure
[Install]
WantedBy=multi-user.target
for i in $(seq 1 $(nproc)); do
systemctl enable --now pgbouncer@$i
done
4. Деление бюджета соединений
Если раньше max_client_conn был, скажем, 1000 на единственный процесс — при флоте из 8 процессов раздели его на восемь (~125 на процесс), чтобы суммарный лимит остался прежним и Postgres не получила больше соединений, чем рассчитана.
Почему это важно для малого/среднего self-hosted проекта
Для типичного VPS с 4-8 ядрами, где крутится Postgres под нагрузкой (интернет-магазин, CRM, аналитика), единственный процесс PgBouncer часто остаётся узким местом ещё до того, как исчерпаны реальные возможности сервера — деньги за неиспользуемые ядра, по сути, платятся впустую. Флот из нескольких процессов на SO_REUSEPORT — бесплатное увеличение пропускной способности без апгрейда самого сервера.
Обсуждение
Комментариев пока нет — будьте первым.
Комментарии публикуются после проверки модератором.