이 기사에서는 하드웨어의 이상적인 동시성을 계산하기 위해 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, "Connection Semantics" 섹션, 2026년 8월 24일 액세스)에서 이 메커니즘을 정확하게 설명합니다.
이 선택에는 실질적인 이점이 있습니다. 한 연결의 충돌이 다른 연결에 영향을 주지 않으며 각 프로세스가 서버의 나머지 부분과 격리됩니다. 또한 직접적인 비용도 발생합니다. 각각의 추가 연결은 자체 메모리 공간과 커널에 대한 자체 컨텍스트 전환 오버헤드를 포함하여 예약할 전체 OS 프로세스를 추가합니다.
풀링 없이 Postgres에 500개의 직접 연결을 여는 애플리케이션은 서버가 500개의 동시 시스템 프로세스를 관리하도록 강제합니다. 이는 대다수가 두 요청 사이에 유휴 상태로 남아 있더라도 마찬가지입니다.
연결이 실제로 소비하는 것: 공유 메모리와 work_mem
두 가지 서로 다른 메커니즘이 기억에 영향을 미치며, 이를 혼동하면 거의 항상 오진이 발생합니다.
첫 번째는 고정되어 있습니다. 시작 시 Postgres는 이러한 연결이 이후에 열리는지 여부에 관계없이 max_connections 값에 따라 크기가 지정된 공유 메모리 구조(잠금, 프로세스 테이블)를 예약합니다. 설정에 대한 공식 문서에서는 이 설정을 늘리면 OS의 기본 구성에서 허용하는 것보다 더 많은 시스템 공유 메모리가 필요할 수 있다고 명시합니다(postgresql.org/docs/current/runtime-config-connection.html, 2026년 8월 24일 액세스).
두 번째는 가변적이며 규모면에서 훨씬 더 위험합니다. work_mem는 연결당 한 번 할당되지 않고 쿼리 계획의 정렬 또는 해싱 작업당 한 번 할당됩니다. 공식 문서는 이 점에 대해 명시적으로 설명합니다. 복잡한 쿼리는 여러 작업을 병렬로 시작할 수 있고 여러 세션이 동시에 동일한 작업을 수행할 수 있으므로 실제로 사용되는 메모리는 work_mem(postgresql.org/docs/current/runtime-config-resource.html, 2026년 8월 24일 액세스)의 몇 배 가치가 될 수 있습니다.
서버의 메모리를 위협하는 것은 max_connections × work_mem만이 아닙니다. max_connections × work_mem × 쿼리당 동시 작업 수입니다. 서버가 스왑되거나 max_connections 증가 후 메모리가 부족해지는 현상을 무해하다고 설명하는 제품입니다.
PostgreSQL 위키 크기 조정 공식
공식 PostgreSQL 프로젝트 위키에는 총 열려 있는 연결 수가 아니라 하드웨어가 병렬로 효율적으로 처리할 수 있는 활성 연결 수를 계산하기 위한 벤치마크 공식이 문서화되어 있습니다(wiki.postgresql.org/wiki/Number_Of_Database_Connections, 2026년 8월 24일 액세스).
이상적인 활성 동시성 = (물리적 코어 × 2) + 효율적인 디스크. 코어 수에는 하이퍼스레딩이 제외됩니다. 별도의 물리적 디스크(“스핀들”)라는 개념이 원래 의미를 많이 상실한 최신 SSD 스토리지에서는 유효 디스크의 수가 1에 가깝게 유지됩니다.
8개의 물리적 코어와 SSD 스토리지가 있는 서버에서 공식은 처리량이 저하되기 전에 (8 × 2) + 1 = 17개의 활성 연결을 제공합니다. 이 수치는 종종 놀랍습니다. 실제로 애플리케이션이 여는 수백 개의 연결에 비하면 작은 것처럼 보입니다. 이것이 바로 다음 단락의 주제입니다.
The number calculated by the formula measures the concurrency that the CPU and disk can absorb, not the number of client connections your application needs to open. A fleet of 20 application processes, each with its own pool of 10 connections, opens 200 simultaneous connections to Postgres even if only 17 of them are actively working at any given time. Without a pooler, max_connections must cover the 200, not the 17. It is this gap that pushes most architectures to add a pooler in transaction mode, even if it means choosing which one (see our comparison PgBouncer, Supavisor and PgCat).
max_connections를 변경하는 방법(및 재부팅이 필요한 이유)
max_connections는 핫스왑되지 않습니다. 이것은 postmaster 컨텍스트 매개변수입니다. Postgres는 시작 시 이를 한 번 읽어 공유 메모리 크기를 조정합니다. 구성 다시 로드(pg_reload_conf() 또는 SIGHUP)로는 충분하지 않습니다. 서버를 다시 시작해야 합니다.
먼저 현재 값과 해당 컨텍스트를 확인하여 다시 시작해야 하는지 확인하세요.
그런 다음 새 값을 적용하고 다시 시작합니다.
max_connections에는 기본적으로 superuser_reserved_connections(기본적으로 3)가 포함됩니다. 이러한 연결은 포화 시 수퍼유저를 위해 예약되어 있으며 글로벌 카운터에 아직 도달하지 않은 경우에도 애플리케이션에서 사용할 수 없습니다.
Aurabase가 Postgres 클러스터에서 max_connections 예산을 책정하는 방법
max_connections 크기 조정은 단순한 이론적인 연습이 아닙니다. Aurabase가 관리형 Postgres 클러스터에 예산을 책정하는 방법은 다음과 같습니다.
전용 클러스터: 프로젝트당 Postgres 클러스터 1개
이 수준에서( 전용 및 공유 기반비교 참조) 각 프로젝트는 인스턴스 크기에 맞는 자체 CloudNativePG 클러스터와 자체 max_connections 예산을 받습니다.
| 무료 (전용) | max_connections 50 | 인스턴스 1개 · vCPU 5억개 · 512Mi |
|---|---|---|
| 프로(기본값) | max_connections 200 | 인스턴스 2개 · vCPU 1개 · 2Gi |
| 팀 | max_connections 300 | 인스턴스 3개 · vCPU 2개 · 3Gi |
| 사업 | max_connections 400 | 인스턴스 3개 · vCPU 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서비스)에 직접 연결된 상태로 유지됩니다. 트랜잭션 모드에서 풀링하면 pgrst라는 전용 LISTEN 채널을 수신하는 스키마 재로드 메커니즘이 중단됩니다. 따라서 자체 연결(공유 수준의 복제본당 2개, 전용 수준의 복제본당 10개)은 풀러 외부에 있는 기본의 max_connections 예산에서 직접적으로 아래 절차의 1단계에 포함되어야 하는 "잊혀진" 연결 종류로 직접 계산됩니다.
코드는 이러한 공유 풀러 예산을 게시된 벤치마크의 고정 수치가 아닌 로드 상태에서 pg_stat_activity를 측정하여 실제 조건에서 보정할 시작 값으로 명시적으로 문서화합니다. 이는 벤치마크 방법론에 설명된 것과 동일한 원칙입니다. 조정하기 전에 측정하고 추측하지 말고 희망하세요. 이러한 클러스터는 Postgres 16 대 17 대 18비교에 문서화된 선택 사항인 PostgreSQL 16에서 실행됩니다.
풀링 없이 max_connections 크기를 조정하는 5단계 절차
이 절차는 특정 도구에 의존하지 않으며 관리되거나 자체 호스팅되는 모든 Postgres 서버에 적용됩니다.
- 실제 클라이언트 연결을 계산합니다. 내부 풀의 크기와 관리 도구, 복제 및 모니터링을 곱한 애플리케이션 프로세스 수입니다. max_connections 층을 설정하는 것은 공식이 아니라 이 숫자입니다.
- PostgreSQL 위키의 공식(물리적 코어 × 2) + 효율적인 디스크를 사용하여 하드웨어의 이상적인 동시성을 계산하십시오. 이 그림은 처리량을 저하시키지 않고 실제로 병렬로 작동할 수 있는 연결 수를 나타냅니다.
superuser_reserved_connections및 애플리케이션 외부에서 자체 연결을 여는 모든 관리 도구에 대한 여유를 두고 1단계의 실제 요구 사항 이상으로 max_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를 위험에 빠뜨리지 않고 할당할 수 있는 사용 가능한 메모리를 계속 초과하고 있습니다.
이 세 가지 경우에서 정답은 거의 항상 더 높은 max_connections가 아니라 애플리케이션과 Postgres 사이에 위치한 풀러입니다. 비교 PgBouncer, Supavisor 및 PgCat에서는 세 가지 옵션을 자세히 설명하고 트랜잭션 모드 가이드에서는 풀러가 설치된 후 가장 일반적인 절충안을 설명합니다. 연결 이상의 모든 Postgres 조정에 대해서는 프로덕션 Postgres 조정 체크리스트를 참조하세요.