한양과학기술고등학교

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

커넥션 풀과 트랜잭션

pool_size max_overflow begin N+1 isolation

세션 수명, 커넥션 개수, 트랜잭션 경계를 이해하면 'connection timeout'·'too many clients' 를 피할 수 있습니다.

1. 풀 파라미터

파라미터의미기본
pool_size상시 유지 커넥션 수5
max_overflow피크 시 추가 허용10
pool_timeout커넥션 대기 최대 초30
pool_recycleN초 지난 커넥션 폐기 (방화벽/서버 타임아웃 대비)-1
pool_pre_ping빌릴 때 살아있는지 확인False

2. 실제 커넥션 상한 계산

# Gunicorn 워커 4개, 각 워커 pool_size=5 + max_overflow=10
#  -> 최대 4 * (5 + 10) = 60 커넥션
#
# PostgreSQL 기본 max_connections = 100
#  -> 다른 앱들과 합쳐 넘지 않는지 확인:  SHOW max_connections;
권장
  • 워커 수를 늘리면 pool_size 는 줄인다
  • 커넥션이 정말 많이 필요하면 PgBouncer(트랜잭션 풀링) 를 앞에 둔다
  • asyncpg 는 PgBouncer transaction 모드와 statement_cache_size=0 필요

3. 트랜잭션 경계

# 방식 A: 세션이 알아서 (commit 시 트랜잭션 종료)
def transfer(db: Session):
    db.add(a); db.add(b)
    db.commit()          # 전부 함께 커밋, 실패 시 rollback 필요

# 방식 B: 명시적 블록 (권장)
def transfer(db: Session):
    with db.begin():                 # 블록 정상 종료 -> commit, 예외 -> rollback
        db.add(a)
        db.add(b)

4. 세션 = 요청 1개

원칙
  • 세션을 전역/모듈 레벨로 공유하지 말 것 (스레드·태스크 안전하지 않음)
  • get_db 의존성으로 요청마다 새 세션, 끝나면 닫기
  • 백그라운드 태스크는 자기 세션을 새로 열어야 함 (요청 세션은 이미 닫힘)

5. N+1 쿼리

# 나쁨: 게시글 20개 -> author 접근마다 쿼리 21번
for p in posts:
    print(p.author.name)

# 좋음: JOIN/추가쿼리 1번으로
from sqlalchemy.orm import selectinload, joinedload
stmt = select(Post).options(selectinload(Post.author))

6. 상태 점검 쿼리

-- 현재 연결 수
SELECT count(*) FROM pg_stat_activity WHERE datname = 'school';
-- 오래 걸리는 쿼리
SELECT pid, now()-query_start AS dur, state, query
FROM pg_stat_activity WHERE state <> 'idle' ORDER BY dur DESC;
"느린 것은 async 로, 무거운 것은 백그라운드로"