В этой статье приводится формула, опубликованная в вики PostgreSQL для расчета идеального одновременного выполнения вашего оборудования (наиболее цитируемая формула определения размера пула соединений в экосистеме), объясняется, почему каждое соединение стоит больше, чем поток приложения, а затем без догадок подробно описана процедура установки max_connections. Наша методология тестирования документирует протокол измерений, используемый для любых заявлений о производительности в этом блоге.
Самое необходимое
- Без объединения в пул max_connections должен охватывать все одновременные клиентские соединения, а не только те, которые Postgres может эффективно обрабатывать параллельно.
- Справочная формула вики-сайта PostgreSQL: идеальный активный параллелизм = (физические ядра × 2) + эффективные диски. Отправная точка, которая должна быть подтверждена измерениями, а не жесткий предел.
- max_connections — это параметр контекста
postmaster: его изменение требует полного перезапуска сервера, а не простой перезагрузки. - Каждое соединение Postgres — это отдельный системный процесс, а не легкий поток: именно это делает накладные расходы реальными, как только количество соединений увеличивается.
- Проверено в коде: в своих выделенных кластерах Postgres Aurabase варьирует max_connections от 50 (уровень бесплатного пользования) до 400 (уровень предприятия) в зависимости от размера кластера.
Почему соединение Postgres стоит дороже, чем поток приложения
Postgres не использует облегченный пул потоков для своих соединений. Каждое подключение клиента запускает полноценный системный процесс.
Процесс postmaster создает новый («вилку») для каждой попытки подключения, посвященный этому единственному сеансу, пока он не будет закрыт. Официальная документация проекта описывает именно этот механизм в главе, посвященной основам архитектуры (postgresql.org/docs/current/connect-estab.html, раздел «Семантика соединения», по состоянию на 24 августа 2026 г.).
У этого выбора есть реальное преимущество: сбой одного соединения не влияет на другие, каждый процесс изолирован от остального сервера. Это также имеет прямую стоимость: каждое дополнительное соединение добавляет в расписание целый процесс ОС со своим собственным пространством памяти и собственными накладными расходами на переключение контекста для ядра.
Приложение, открывающее 500 прямых подключений к Postgres без объединения в пул, заставляет сервер управлять 500 одновременными системными процессами, даже если подавляющее большинство из них простаивают между двумя запросами.
Что на самом деле потребляет соединение: общая память и work_mem
Два различных механизма влияют на память, и их путаница почти всегда приводит к неправильному диагнозу.
Первое исправлено. При запуске Postgres резервирует структуры общей памяти (блокировки, таблица процессов), размер которых равен значению max_connections, независимо от того, будут ли впоследствии открыты эти соединения. В официальной документации по этому параметру прямо указано: для его увеличения может потребоваться больше общей системной памяти, чем позволяет конфигурация вашей ОС по умолчанию (postgresql.org/docs/current/runtime-config-connection.html, по состоянию на 24 августа 2026 г.).
Второй является переменным и гораздо более опасен в масштабе: work_mem выделяется не один раз для каждого соединения, а один раз для каждой операции сортировки или хеширования в плане запроса. Официальная документация ясно говорит на этот счет: сложный запрос может запускать несколько таких операций параллельно, и несколько сеансов могут делать то же самое одновременно, так что фактически используемая память может стоить в несколько раз больше work_mem (postgresql.org/docs/current/runtime-config-resource.html, по состоянию на 24 августа 2026 г.).
Не только max_connections × work_mem угрожает памяти сервера. Это max_connections × work_mem × количество одновременных операций на запрос. Именно этот продукт объясняет сервер, который меняет местами или у которого заканчивается память после увеличения max_connections, которое считается безобидным.
Формула определения размера вики PostgreSQL
В официальной вики-странице проекта PostgreSQL документирована эталонная формула для расчета количества активных соединений , которое ваше оборудование может эффективно обрабатывать параллельно, а не количества открытых соединений в целом (wiki.postgresql.org/wiki/Number_Of_Database_Connections, по состоянию на 24 августа 2026 г.).
идеальный активный параллелизм = (физические ядра × 2) + эффективные диски. Количество ядер исключает гиперпоточность. Число эффективных дисков в современных SSD-накопителях остается близким к 1, где понятие отдельного физического диска («шпинделя») теряет большую часть своего первоначального значения.
На сервере с 8 физическими ядрами и SSD-накопителем формула дает (8 × 2) + 1 = 17 активных подключений, прежде чем пропускная способность начнет снижаться. Эта цифра часто удивляет: она кажется крохой по сравнению с сотнями соединений, которые приложение открывает на практике. Именно этому посвящен следующий абзац.
Число, рассчитанное по формуле, измеряет параллелизм, который могут обеспечить ЦП и диск, а не количество клиентских подключений, которые необходимо открыть вашему приложению. Группа из 20 прикладных процессов, каждый из которых имеет собственный пул из 10 подключений, открывает 200 одновременных подключений к Postgres, даже если в любой момент времени активно работают только 17 из них. Без пулера max_connections должно охватывать 200, а не 17. Именно этот пробел подталкивает большинство архитектур добавлять пулер в режиме транзакций, даже если это означает выбор того, какой из них (см. наше сравнение PgBouncer, Supavisor и PgCat).
Как изменить max_connections (и зачем нужна перезагрузка)
max_connections не подлежит горячей замене. Это параметр контекста postmaster: Postgres читает его один раз при запуске, чтобы определить размер своей общей памяти. Перезагрузки конфигурации (pg_reload_conf() или SIGHUP) недостаточно, необходимо перезагрузить сервер.
Сначала проверьте текущее значение и его контекст, чтобы убедиться в необходимости перезагрузки:
Затем примените новое значение и перезапустите:
max_connections по умолчанию включает superuser_reserved_connections (3 по умолчанию): эти соединения зарезервированы для суперпользователя в случае насыщения, они никогда не доступны для вашего приложения, даже если глобальный счетчик еще не достигнут.
Как Aurabase распределяет max_connections в своих кластерах Postgres
Определение размера max_connections — это не просто теоретическое упражнение. Вот как Aurabase планирует это в своих управляемых кластерах Postgres:
Выделенные кластеры: один кластер Postgres на проект.
На этом уровне (см. наше сравнение выделенной и общей базы) каждый проект получает собственный кластер CloudNativePG и собственный бюджет max_connections, размер которого зависит от размера экземпляра:
| бесплатный (выделенный) | max_connections 50 | 1 экземпляр · 500 млн виртуальных ЦП · 512Mi |
|---|---|---|
| про (по умолчанию) | max_connections 200 | 2 экземпляра · 1 виртуальный ЦП · 2Gi |
| команда | max_connections 300 | 3 экземпляра · 2 виртуальных ЦП · 3Gi |
| бизнес | max_connections 400 | 3 экземпляра · 2 виртуальных ЦП · 4Gi |
Общие кластеры: несколько проектов организации, общий бюджет.
По этому второму пути все проекты одной организации подключаются через пул CNPG (PgBouncer, режим transaction) перед общим первичным сервером:
| бесплатно | max_connections 50 | max_client_conn 100 | max_user_connections 20 |
|---|---|---|---|
| про | max_connections 100 | max_client_conn 200 | max_user_connections 60 |
| команда | max_connections 200 | max_client_conn 400 | max_user_connections 150 |
Все проекты в организации соединяются через общую роль приложения. Таким образом, max_user_connections ограничивает общее количество подключений к серверу, которые эта роль может открыть во всем кластере: это настоящая глобальная защита кластера, а не max_client_conn, который ограничивает только клиентские подключения к самому пулу.
Однако этот пулер обслуживает только трафик приложений SDK. PostgREST, со своей стороны, остается напрямую подключенным к основному сервису (-rw): объединение в пул в режиме транзакций нарушит механизм перезагрузки схемы, который прослушивает выделенный канал LISTEN с именем pgrst. Поэтому его собственные соединения (2 на реплику на общем уровне, 10 на реплику на выделенном уровне) напрямую учитываются в бюджете max_connections первичного сервера, за пределами какого-либо пулера, именно тот тип «забытого» соединения, который должен включать шаг 1 процедуры ниже.
Код явно документирует эти общие бюджеты пулеров как начальные значения, подлежащие калибровке в реальных условиях путем измерения pg_stat_activity под нагрузкой, а не как фиксированные значения из опубликованного теста. Это та же самая дисциплина, которая описана в нашей методологии тестирования : измеряйте, прежде чем корректировать, а не гадать, а затем надеяться. Эти кластеры работают на PostgreSQL 16 — выбор, описанный в нашем сравнении Postgres 16, 17 и 18.
5-шаговая процедура определения размера max_connections без объединения в пул
Эта процедура не зависит от какого-либо конкретного инструмента: она применима к любому серверу Postgres, управляемому или локальному.
- Подсчитайте количество фактических клиентских подключений. Количество процессов приложения, умноженное на размер их внутреннего пула, плюс инструменты администрирования, репликации и мониторинга. Именно это число, а не формула, устанавливает минимальный уровень max_connections.
- Рассчитайте идеальную параллельную работу вашего оборудования по формуле из вики PostgreSQL: (физические ядра × 2) + эффективные диски. Эта цифра показывает, сколько из этих соединений действительно могут работать параллельно без снижения пропускной способности.
- Установите max_connections выше фактической потребности для шага 1, с запасом для
superuser_reserved_connectionsи для любых инструментов администрирования, которые открывают свои собственные соединения вне приложения. - Примените изменения с помощью ALTER SYSTEM SET, затем перезапустите сервер. Это параметр postmaster: простой перезагрузки недостаточно, как подробно описано выше.
- Мониторинг pg_stat_activity с течением времени. Если количество простаивающих соединений значительно превышает количество активных соединений, это не проблема max_connections: это сигнал о том, что вам нужен пулер перед сервером, а не большее число.
Запрос мониторинга из шага 5, который можно использовать напрямую:
Когда формулы уже недостаточно: признаки того, что вам нужен пулер
Три сигнала систематически возвращаются, когда одного max_connections уже недостаточно, каким бы ни было его значение.
- Ошибка
FATAL: sorry, too many clients alreadyпоявляется во время пиковой нагрузки, тогда как большинство подключений, отображаемыхpg_stat_activity, находятся в состоянии ожидания. - Приложение работает в бессерверной среде или с эфемерными рабочими процессами (граничными функциями, короткими заданиями), которые открывают и закрывают соединения гораздо быстрее, чем была разработана для модели Postgres «процесс на соединение».
- Вышеупомянутая формула и процедура уже были применены, и фактическая потребность в клиентских соединениях продолжает превышать объем доступной памяти, который можно выделить, не ставя под угрозу work_mem или Shared_buffers.
В этих трех случаях правильный ответ почти всегда — это пулер, расположенный между приложением и Postgres, а не более высокий max_connections. В нашем сравнении PgBouncer, Supavisor и PgCat подробно описаны три варианта, а в нашем руководстве по режиму транзакций объясняется наиболее распространенный компромисс после установки пулера. Все настройки Postgres, помимо соединений, см. в нашем контрольном списке настройки производственного Postgres .