트래픽이 조금만 몰리면 API가 이 에러를 뱉으면서 죽습니다.
FATAL: sorry, too many clients alreadyPostgreSQL max_connections는 기본 100입니다. 서버 인스턴스를 4개로 오토스케일하는데, 각 인스턴스가 커넥션 풀을 50개씩 잡게 해놨어요. 계산해보니 4×50=200이라 100을 넘네요. 그냥 max_connections를 500으로 올리면 될까요?
트래픽이 조금만 몰리면 API가 이 에러를 뱉으면서 죽습니다.
FATAL: sorry, too many clients alreadyPostgreSQL max_connections는 기본 100입니다. 서버 인스턴스를 4개로 오토스케일하는데, 각 인스턴스가 커넥션 풀을 50개씩 잡게 해놨어요. 계산해보니 4×50=200이라 100을 넘네요. 그냥 max_connections를 500으로 올리면 될까요?
댓글 4
원인 진단은 정확하십니다(4×50=200 > 100). 그런데 max_connections를 무작정 올리는 건 함정이에요. 커넥션 하나하나가 PostgreSQL에선 별도 프로세스라 메모리를 먹습니다. 500으로 올리면 커넥션 폭주 시 이번엔 DB 서버가 메모리로 죽어요.
먼저 — 풀 크기를 줄이세요인스턴스당 50은 과합니다. 유명한 공식이 있어요: 커넥션 수 ≈ (코어 수 × 2) + 디스크 수. 대부분의 앱은 인스턴스당 10개면 충분합니다.
- 4 인스턴스 × 10 = 40 < 100 → 여유 있게 해결
- 커넥션은 많을수록 좋은 게 아닙니다. DB가 동시에 진짜로 처리하는 쿼리는 코어 수에 묶여 있어서, 풀만 키우면 컨텍스트 스위칭으로 오히려 느려져요.
근본 해결 — 커넥션 풀러(PgBouncer)인스턴스가 계속 늘어나는 구조라면 앱마다 DB에 직접 붙지 말고, 앞에 PgBouncer를 두세요.
transaction 풀링 모드면 트랜잭션 단위로 커넥션을 재활용해서, 앱 커넥션 수와 무관하게 DB 커넥션을 아주 적게 유지합니다. 서버리스·오토스케일 환경의 사실상 표준입니다.
transaction 모드의 대표적인 주의점입니다. 이 모드는 매 트랜잭션마다 커넥션을 바꿔 배정하는데, prepared statement는 특정 커넥션(세션)에 묶여 있어서 다음 트랜잭션에서 다른 커넥션을 받으면 "그런 statement 없다"가 됩니다.
세 가지 해법이 있어요.
?pgbouncer=true형태를 지원하는 드라이버도 있음), 클라이언트 설정으로 서버 사이드 prepare를 비활성화합니다.max_prepared_statements를 설정하면 transaction 모드에서도 어느 정도 지원합니다.보통은 드라이버에서 prepared statement 비활성화가 가장 무난합니다. 성능 차이는 대부분의 앱에서 체감되지 않는 수준이고요.