Zettelkasten

DB 커넥션은 풀 크기를 늘리기 전에 트랜잭션 점유시간을 줄여 회전율을 높인다

·수정 1

요약

  • 커넥션 풀 키우기는 공급(커넥션 개수) 을 늘리는 접근, 트랜잭션 분석은 커넥션 점유시간(W) 을 줄여 회전율을 높이는 접근이다.
  • Little's Law(동시 필요 커넥션 ≈ 도착률 λ × 점유시간 W)로 보면 W를 절반으로 줄이면 같은 풀로 2배 처리량을 감당한다.
  • 점유시간을 줄이는 핵심은 트랜잭션 밖으로 외부 I/O 빼기, lazy connection, read-only 분리, 트랜잭션 경계 축소.

본문

두 축: 공급 vs 점유시간

커넥션 풀 도입/확대는 커넥션 개수라는 공급을 늘리는 것이다. 하지만 커넥션은 DB 쪽 메모리·CPU·락 경합 비용을 동반하므로 무한정 늘릴 수 없다. 다른 축은 커넥션 하나를 잡고 있는 시간을 줄여 회전율을 높이는 것 — 이게 트랜잭션 분석의 영역이다.

Little's Law로 본 정량적 근거

동시에 필요한 커넥션 수 ≈ 요청 도착률(λ) × 커넥션 평균 점유시간(W)

점유시간 W를 절반으로 줄이면 풀 크기를 안 늘려도 같은 풀로 2배의 처리량을 감당한다. "풀 키우면 되지"보다 한 단계 깊은 답인 이유.

W를 줄이는 구체적 방법

1. 트랜잭션 안에서 외부 I/O 빼기 (효과 가장 큼) 트랜잭션이 열린 동안은 커넥션을 계속 점유한다. 그 안에 외부 API 호출·메시지 발행·무거운 연산·파일 I/O가 들어있으면 그 시간만큼 커넥션을 쥔 채 논다.

@Transactional {
  query A
  callExternalApi()   // 300ms 동안 커넥션 점유한 채 대기
  query B
}

→ 외부 호출을 트랜잭션 밖으로 빼거나 트랜잭션을 쪼갠다.

2. Lazy connection acquisition Spring LazyConnectionDataSourceProxy. 트랜잭션을 시작해도 실제 첫 쿼리가 나갈 때까지 커넥션을 빌리지 않는다. 트랜잭션 시작~첫 쿼리 사이의 빈 점유 구간 제거. read/write 라우팅과도 잘 맞음.

3. 읽기/쓰기 분리 + read-only 트랜잭션 readOnly=true 표시 → 리플리카 라우팅으로 primary 부담 감소, DB가 flush/dirty checking 생략. 단건 SELECT에 불필요한 트랜잭션 제거도 같은 맥락.

4. 트랜잭션 경계 축소 서비스 메서드 하나에 검증·조회·외부연동·쓰기가 다 묶여있으면, 정말 원자적이어야 하는 구간만 트랜잭션으로 좁힌다.

5. 트랜잭션 내 round-trip 줄이기 N+1 제거, batch insert/update, fetch join. DB 왕복이 줄면 점유시간도 짧아진다.

6. 격리 수준 점검 필요 이상의 isolation level은 락을 오래 잡아 점유시간·동시성에 악영향. 비즈니스가 허용하는 선에서 낮춘다.

출발점은 모니터링

어떤 트랜잭션이 커넥션을 오래 쥐는지 측정하는 게 먼저다. pool 크기를 늘리는 건 공급, 점유시간을 줄이는 건 수요 측 최적화이며 DB 부하 측면에서 후자가 더 근본적이다.

관련 노트

참고