본문 바로가기
Q&A2026년 7월 8일1분 읽기

PostgreSQL이 too many connections로 터집니다 (FATAL: sorry, too many clients)

b
backend_junseo
조회 1759댓글 4

트래픽이 조금만 몰리면 API가 이 에러를 뱉으면서 죽습니다.

FATAL: sorry, too many clients already

PostgreSQL max_connections는 기본 100입니다. 서버 인스턴스를 4개로 오토스케일하는데, 각 인스턴스가 커넥션 풀을 50개씩 잡게 해놨어요. 계산해보니 4×50=200이라 100을 넘네요. 그냥 max_connections를 500으로 올리면 될까요?

댓글 4

김김영삼3개월 전

원인 진단은 정확하십니다(4×50=200 > 100). 그런데 max_connections를 무작정 올리는 건 함정이에요. 커넥션 하나하나가 PostgreSQL에선 별도 프로세스라 메모리를 먹습니다. 500으로 올리면 커넥션 폭주 시 이번엔 DB 서버가 메모리로 죽어요.

먼저 — 풀 크기를 줄이세요

인스턴스당 50은 과합니다. 유명한 공식이 있어요: 커넥션 수 ≈ (코어 수 × 2) + 디스크 수. 대부분의 앱은 인스턴스당 10개면 충분합니다.

  • 4 인스턴스 × 10 = 40 < 100 → 여유 있게 해결
  • 커넥션은 많을수록 좋은 게 아닙니다. DB가 동시에 진짜로 처리하는 쿼리는 코어 수에 묶여 있어서, 풀만 키우면 컨텍스트 스위칭으로 오히려 느려져요.
근본 해결 — 커넥션 풀러(PgBouncer)

인스턴스가 계속 늘어나는 구조라면 앱마다 DB에 직접 붙지 말고, 앞에 PgBouncer를 두세요.

# 앱들 → PgBouncer(transaction 모드) → PostgreSQL
# 앱은 수백 개 커넥션을 열어도
# PgBouncer가 실제 DB에는 20~30개만 유지

transaction 풀링 모드면 트랜잭션 단위로 커넥션을 재활용해서, 앱 커넥션 수와 무관하게 DB 커넥션을 아주 적게 유지합니다. 서버리스·오토스케일 환경의 사실상 표준입니다.

bbackend_junseo(guest)3개월 전
PgBouncer를 transaction 모드로 붙였는데, 앱에서 쓰던 prepared statement가 깨진다는 에러가 나요. 이건 왜 그런가요?
김김영삼3개월 전

transaction 모드의 대표적인 주의점입니다. 이 모드는 매 트랜잭션마다 커넥션을 바꿔 배정하는데, prepared statement는 특정 커넥션(세션)에 묶여 있어서 다음 트랜잭션에서 다른 커넥션을 받으면 "그런 statement 없다"가 됩니다.

세 가지 해법이 있어요.

  • 드라이버 쪽에서 prepared statement 끄기 — 가장 간단합니다. 예를 들어 접속 문자열에 옵션을 주거나(?pgbouncer=true 형태를 지원하는 드라이버도 있음), 클라이언트 설정으로 서버 사이드 prepare를 비활성화합니다.
  • PgBouncer 1.21+의 prepared statement 지원 켜기 — 최근 버전은 max_prepared_statements를 설정하면 transaction 모드에서도 어느 정도 지원합니다.
    # pgbouncer.ini
    max_prepared_statements = 200
  • session 모드로 바꾸기 — prepared statement가 정상 동작하지만, 커넥션 재활용 효율이 떨어져 풀링 이점이 줄어듭니다. 트레이드오프예요.

보통은 드라이버에서 prepared statement 비활성화가 가장 무난합니다. 성능 차이는 대부분의 앱에서 체감되지 않는 수준이고요.

bbackend_junseo(guest)3개월 전
풀 크기부터 잘못 이해하고 있었네요. 인스턴스당 10개로 줄이고 PgBouncer 붙이니 커넥션 에러가 완전히 사라졌습니다. "많을수록 좋은 게 아니다"라는 말이 확 와닿네요. 감사합니다!
Ctrl+Enter로 등록