명령어/DB

[PostgreSQL] "too many clients already" — max_connections와 커넥션 풀링

jykim23 2026. 7. 30. 22:12
반응형

설치·접속: PostgreSQL 설치와 접속

부제: 접속이 한도에 부딪히면 새 연결이 전부 거절된다 — 지금 몇 개 쓰는지 세고, 왜 풀링이 답인지

SELECT current_setting('max_connections') AS max_conn,
       count(*) FILTER (WHERE backend_type='client backend') AS client_conns,
       count(*) AS all_backends,
       current_setting('superuser_reserved_connections') AS reserved
FROM pg_stat_activity;
 max_conn | client_conns | all_backends | reserved
----------+--------------+--------------+----------
 100      |            1 |            6 | 3

여기서 헷갈리는 지점. max_connections=100이지만 이게 전부 앱 몫이 아니다. all_backends=6 중 실제 클라이언트 접속(client backend)은 1개뿐이고 나머지는 서버 내부 프로세스다. 게다가 superuser_reserved_connections=3은 관리자용으로 예약돼 있어, 일반 앱이 실제로 쓸 수 있는 건 대략 100 - 3 = 97개다. 무엇이 슬롯을 먹고 있는지는 backend 종류로 갈라 본다.

SELECT backend_type, count(*) FROM pg_stat_activity
GROUP BY backend_type ORDER BY count DESC;
         backend_type         | count
------------------------------+-------
 client backend               |     1
 walwriter                    |     1
 autovacuum launcher          |     1
 logical replication launcher |     1
 background writer            |     1
 checkpointer                 |     1

too many clients already 에러는 이 슬롯이 동나면 터진다. 흔한 원인은 접속이 진짜 많아서가 아니라, 앱이 커넥션을 열고 안 닫거나(누수) 요청마다 새로 여는 구조라서다. PostgreSQL은 접속 하나가 프로세스 하나라 max_connections를 무작정 올리면 메모리만 먹고 오히려 느려진다. 그래서 답은 한도를 키우는 게 아니라 커넥션 풀러(PgBouncer 등)를 앞에 두고 소수의 실제 접속을 여러 앱이 돌려 쓰게 하는 것이다.

이렇게도 쓴다

한도의 몇 %까지 찼는지 비율로 본다. (조합: 나눗셈)

SELECT count(*) AS used, current_setting('max_connections')::int AS max,
       round(100.0*count(*)/current_setting('max_connections')::int,1) AS pct
FROM pg_stat_activity;

 

접속을 잡아먹는 앱·유저별로 집계한다. (조합: GROUP BY)

SELECT usename, application_name, count(*) FROM pg_stat_activity
WHERE backend_type='client backend' GROUP BY 1,2 ORDER BY count DESC;

 

슬롯만 차지하는 유휴 세션을 골라낸다. (조합: state 필터)

SELECT pid, usename, now()-state_change AS idle_for FROM pg_stat_activity
WHERE state='idle' ORDER BY idle_for DESC;

 

오래 놀고 있는 유휴 트랜잭션을 서버가 자동으로 끊게 한다. (조합: idle_in_transaction_session_timeout)

SET idle_in_transaction_session_timeout = '30s';

 

앞단에 PgBouncer를 두고 트랜잭션 단위로 접속을 재사용한다. (조합: PgBouncer)

[databases]
shop = host=127.0.0.1 dbname=shop
[pgbouncer]
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 20   # 실제 PG 접속은 20개로 1000 클라이언트를 소화

 

특정 유저의 동시 접속 수 자체를 제한한다. (조합: ALTER ROLE)

ALTER ROLE app_user CONNECTION LIMIT 30;
반응형