🔍 검색 키워드: PostgreSQL connection pool 에러, 커넥션 풀 소진, 데이터베이스 연결 오류, 최대 연결 초과
PostgreSQL Connection Pool 고갈 해결하기
증상
애플리케이션이 실행되다가 갑자기 다음과 같은 에러가 발생한다:
FATAL: sorry, too many clients already
또는:
Error: connect ECONNREFUSED - PostgreSQL connection failed
또는 connection pool 라이브러리에서:
Error: timeout acquiring a connection from the pool
Error: Client has already been released to the pool
특히 트래픽이 많아지거나 장시간 실행되는 배치 작업 후에 자주 발생한다. 새로운 요청이 들어와도 DB 연결을 못 하고 응답 불가 상태가 된다.
원인
PostgreSQL은 최대 동시 연결 수 제한이 있고 (기본값 100), connection pool은 재사용 가능한 연결 수를 미리 정해둔다. 고갈 현상은 다음 경우에 발생:
- 연결을 반환 안 함: 쿼리 후 연결을 pool에 반환하지 않아 계속 증가
- 타임아웃 후 좀비 연결: 응답 없는 연결이 pool에 남아있음
- Pool 사이즈 설정 과소: 동시 사용자/요청량에 비해 pool이 너무 작음
- 쿼리 오래 걸림: 느린 쿼리가 연결을 오래 점유
- Connection leak: 예외 발생 시 연결을 close하지 않는 코드
해결방법
1. Connection Pool 설정 최적화
Node.js + pg (node-postgres):
const { Pool } = require('pg');
const pool = new Pool({
host: 'localhost',
port: 5432,
database: 'mydb',
user: 'postgres',
password: 'password',
max: 20, // 최대 연결 수 (기본 10)
idleTimeoutMillis: 30000, // 30초 idle 후 닫기
connectionTimeoutMillis: 2000, // 연결 생성 타임아웃
});
module.exports = pool;
Python + psycopg2:
import psycopg2.pool
connection_pool = psycopg2.pool.SimpleConnectionPool(
1, // minimum connections
20, // maximum connections
database="mydb",
user="postgres",
password="password",
host="localhost"
)
// 사용
conn = connection_pool.getconn()
try:
// 쿼리 실행
pass
finally:
connection_pool.putconn(conn)
Java + HikariCP:
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:postgresql://localhost:5432/mydb");
config.setUsername("postgres");
config.setPassword("password");
config.setMaximumPoolSize(20); // 최대 연결
config.setMinimumIdle(5); // 최소 유휴 연결
config.setConnectionTimeout(2000); // 2초 타임아웃
config.setIdleTimeout(600000); // 10분 idle 타임아웃
config.setMaxLifetime(1800000); // 30분 최대 수명
HikariDataSource dataSource = new HikariDataSource(config);
2. 연결이 제대로 반환되는지 확인
Node.js에서 연결 누수 확인:
const pool = new Pool(config);
pool.on('error', (err, client) => {
console.error('Unexpected error on idle client', err);
process.exit(-1);
});
pool.on('connect', () => {
console.log('New connection created');
});
// 항상 try-finally로 연결 반환 보장
const client = await pool.connect();
try {
const result = await client.query('SELECT * FROM users WHERE id = $1', [1]);
return result.rows;
} finally {
client.release(); // 반드시 실행
}
또는 with 문 활용 (Python):
from contextlib import contextmanager
@contextmanager
def get_db_connection():
conn = connection_pool.getconn()
try:
yield conn
finally:
connection_pool.putconn(conn)
// 사용
with get_db_connection() as conn:
cursor = conn.cursor()
cursor.execute("SELECT * FROM users WHERE id = %s", (1,))
// 예외 발생해도 자동으로 반환됨
3. 느린 쿼리 최적화
-- 인덱스 추가
CREATE INDEX idx_users_id ON users(id);
-- 실행계획 확인
EXPLAIN ANALYZE SELECT * FROM users WHERE status = 'active';
-- 필요 없는 조인 제거, WHERE 조건 최적화
SELECT u.id, u.name
FROM users u
WHERE u.created_at > NOW() - INTERVAL '7 days'
AND u.status = 'active'
LIMIT 100;
4. PostgreSQL 서버 설정 확인
# PostgreSQL 최대 연결 수 조회
psql -U postgres -c "SHOW max_connections;"
# 현재 연결 수 조회
psql -U postgres -c "SELECT count(*) FROM pg_stat_activity;"
# 연결 상세 정보
psql -U postgres -c "SELECT datname, count(*) FROM pg_stat_activity GROUP BY datname;"
필요하면 postgresql.conf에서:
max_connections = 200 # 기본값 100에서 증가
5. 좀비 연결 정리
-- 유휴 연결 종료
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE datname = 'mydb'
AND state = 'idle'
AND query_start < now() - interval '30 minutes';
-- 슬로우 쿼리 강제 종료 (주의)
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE query_start < now() - interval '5 minutes'
AND state != 'idle';
정리표
| 상황 | 원인 | 해결법 |
|---|---|---|
| 연결 고갈 후 다시 증가 | 연결을 close하지 않음 | try-finally 또는 context manager로 보장 |
| Pool 타임아웃 | pool이 너무 작음 | max 크기 증가, idleTimeoutMillis 조정 |
| "too many clients" 에러 | PostgreSQL 최대 연결 도달 | max_connections 증가 또는 app pool 감소 |
| 간헐적 연결 오류 | connectionTimeoutMillis 너무 짧음 | 타임아웃 값 증가 또는 네트워크 확인 |
| 메모리 누수 | 좀비 연결이 메모리 점유 | 주기적으로 idle 연결 정리 (pg_terminate_backend) |
TIP: 프로덕션에서는 반드시 connection pool 크기, 타임아웃, idle 설정을 검토하고, 정기적으로 pg_stat_activity로 모니터링하자.
댓글 없음:
댓글 쓰기