한양과학기술고등학교

에듀테크 프로젝트 대시보드

커넥션 풀

max_connections backend process PgBouncer pool_size

PostgreSQL 의 커넥션 하나 = OS 프로세스 하나. 여는 비용이 크므로 미리 만들어 재사용합니다.

1. 왜 풀이 필요한가

커넥션 비용
  • 연결마다 백엔드 프로세스 fork + 수 MB 메모리 + 인증 왕복
  • max_connections 를 무작정 올리면 컨텍스트 스위칭·메모리로 오히려 느려짐 (보통 상한 100~200)

2. 애플리케이션 풀

# SQLAlchemy
engine = create_engine(url, pool_size=5, max_overflow=5, pool_timeout=30, pool_pre_ping=True)

# asyncpg
pool = await asyncpg.create_pool(dsn, min_size=2, max_size=10)

이 서버의 fastapi-school 은 asyncpg 풀을 lifespan 에서 생성합니다 — FastAPI Lifespan 참고.

3. 상한 계산

이 서버 예시
gunicorn 워커 4 × (pool_size 5 + max_overflow 5) = 40
+ 다른 앱들 + psql·백업·모니터링 여유분
≤ max_connections (100)
주의
  • Gunicorn 워커마다 별도 풀이 생깁니다. lifespan 에서 만든 풀은 프로세스 로컬

4. PgBouncer

클라이언트가 수백~수천이면 PgBouncer(transaction 모드)를 앞에 둬서 실제 PG 커넥션을 수십 개로 압축합니다.

transaction 모드 제약
  • 세션 상태에 의존하는 기능 불가: SET, 세션 advisory lock, LISTEN/NOTIFY, 서버측 prepared statement
  • asyncpg 는 statement_cache_size=0 필요
"EXPLAIN ANALYZE 없이 성능을 논하지 마라"