← На главную

PgBouncer: 1 процесс, ClickHouse Managed Postgres: 16 — 4x транзакций

11.07.2026 15:28 · hackernews

PgBouncer однопоточный. Один процесс упирается в одно ядро CPU, сколько бы их ни было на машине. На 16-ядерном сервере это значит, что 15 ядер простаивают, а пулер сам становится узким местом задолго до того, как упрётся Postgres.

В ClickHouse Managed Postgres проблему решили флотом процессов PgBouncer. Количество процессов подбирается под число ядер. Каждый процесс в этом флоте слушает один и тот же порт благодаря опции so_reuseport. Ядро само распределяет входящие соединения между процессами. Клиенты видят один эндпоинт и не догадываются, что за ним целая группа пулеров. Именно на такой монтаж ссылается документация самого PgBouncer, если нужно задействовать больше одного ядра.

С cancel-запросами есть нюанс. Postgres присылает отмену запроса на новое соединение, и so_reuseport может отдать его процессу, который ничего не знает о висящем запросе. Решается это «пирингом»: процессы знают друг о друге и пересылают cancel-запрос туда, где выполняется исходная сессия. Отмена работает по всему флоту.

Пул работает в transaction mode, так что соединение с сервером возвращается в пул сразу после завершения транзакции. Лимиты max_client_conn и max_db_connections делятся на количество процессов, чтобы флот в сумме не перегрузил Postgres.

Сравнили на одинаковых AWS EC2 c7i.4xlarge (16 vCPU). Одна машина крутила один PgBouncer, вторая — 16 процессов. Postgres и нагрузка (pgbench, select-only, transaction-pooled) были идентичными. Единственная разница — один процесс против флота.

Одиночный процесс упёрся в потолок около 87k транзакций в секунду, а при 256 клиентах даже просел до 77k — всё упиралось в одно ядро. Флот же разогнался до 336k транзакций/сек (примерно в 4 раза больше) — у него были свободные ядра. pidstat показал, что одинокий PgBouncer сидит на 97% CPU, а вся 16-ядерная машина загружена меньше чем на 10%. Флот загрузил около 8 ядер и упёрся уже в возможности Postgres и генератора нагрузки, а не в пулер.

Облачная метрика CPUUtilization от CloudWatch подтверждает: при 256 клиентах одноядерная конфигурация даёт 16% утилизации CPU, флотовая — около 60%. Разница в 4 раза. Картина та же: вы платите за 16 vCPU, а один PgBouncer использует лишь крохи.

При малом числе коннекторов один процесс даже чуть быстрее — нечего распараллеливать. Но разрыв начинается там, где реальная нагрузка упирается в одно ядро. ClickHouse Managed Postgres использует эту схему по умолчанию: флот, so_reuseport и пиринг превращают пулер из узкого места в просто трубу.

Читать оригинал →