DB·SQL 실무 가이드 · Part 16

커넥션 풀과 트랜잭션 경계가 DB에 남기는 흔적

화면이 멈췄는데 DB는 한가할 때 어디를 봐야 하는지 알기

작성 기준2026년 7월버전과 지원 현황은 이후 달라질 수 있으니 공식 문서를 함께 확인하세요.

이 파트에서 다루는 내용

고갈의 증상고갈로 가는 경로풀 크기 판단긴 트랜잭션의 대가세션 대응표
01

화면은 멈췄는데 DB는 한가한 상황

장애 신고를 받고 DB를 봤는데 실행 중인 무거운 쿼리도 없고 락 대기도 없습니다. 그런데 애플리케이션은 응답하지 않습니다.

이 조합이 나오면 커넥션 풀을 의심합니다. DB는 놀고 있는데 애플리케이션이 DB에 접근할 통로를 확보하지 못하고 있는 상태입니다.

DB 쪽에서 보이는 모양은 뚜렷합니다. 세션은 많은데 대부분 INACTIVE이고, 마지막 작업 이후 시간이 길게 늘어져 있습니다.

세션 상태 분포 확인sql
SELECT username,
       program,
       status,
       COUNT(*)                 AS session_cnt,
       ROUND(AVG(last_call_et)) AS avg_idle_sec,
       MAX(last_call_et)        AS max_idle_sec
FROM   v$session
WHERE  username IS NOT NULL
GROUP BY username, program, status
ORDER BY session_cnt DESC;

INACTIVE 세션 수가 풀 최대치에 가깝고 idle 시간이 길면 커넥션이 반납되지 않고 있는 것입니다. 정상이라면 풀의 커넥션은 짧게 쓰이고 바로 돌아옵니다.

트랜잭션을 열어 둔 채 놀고 있는 세션sql
SELECT s.sid,
       s.serial#,
       s.username,
       s.status,
       s.last_call_et AS idle_sec,
       s.machine,
       s.module,
       t.start_time   AS tx_start,
       t.used_ublk    AS undo_blocks
FROM   v$session s
JOIN   v$transaction t ON t.ses_addr = s.saddr
WHERE  s.status = 'INACTIVE'
ORDER BY s.last_call_et DESC;

여기 잡히는 세션이 가장 위험합니다. 아무 일도 안 하면서 트랜잭션을 열어 두고 있어서 락과 UNDO를 계속 붙들고 있습니다. Part 11의 락 문제와 Part 10의 ORA-01555가 여기서 시작됩니다.

02

고갈로 가는 경로는 몇 가지로 정해져 있습니다

트랜잭션 안의 외부 호출
가장 흔함

커넥션을 잡은 채 외부 API를 부릅니다. 상대가 느려지면 그 시간만큼 커넥션이 묶입니다. 상대 시스템 장애가 우리 시스템 장애가 되는 경로입니다.

커밋 누락
코드 결함

예외 경로에서 커밋도 롤백도 하지 않고 빠져나가면 커넥션이 트랜잭션을 문 채 반납됩니다. 위 두 번째 쿼리에 잡힙니다.

커넥션 누수
직접 사용 시

JDBC를 직접 쓰면서 닫지 않으면 반납되지 않습니다. 프레임워크를 쓰면 대부분 방지되지만 직접 커넥션을 얻는 코드가 남아 있으면 여기서 샙니다.

느린 쿼리 누적
간접 원인

쿼리 하나가 느려지면 그것을 기다리는 커넥션이 쌓여 풀이 마릅니다. 이 경우는 ACTIVE 세션이 많으므로 앞의 경우와 구분됩니다.

긴 배치와 온라인 공유
설계 문제

배치가 같은 풀을 쓰면 온라인 요청이 밀립니다. 성격이 다른 작업은 풀을 분리하는 것이 안전합니다.

사용자 입력 대기
설계 금기

화면을 열어 둔 동안 커넥션이나 트랜잭션을 유지하면 안 됩니다. Part 10에서 다룬 낙관적 잠금으로 풀어야 할 문제입니다.

03

풀을 키우면 해결된다고 생각하기 쉽습니다

커넥션이 부족하다는 오류를 보면 풀 크기부터 늘리게 됩니다. 대개는 원인을 덮을 뿐이고, 때로는 더 나빠집니다.

DB에는 동시에 붙을 수 있는 세션의 상한이 있습니다. 애플리케이션 인스턴스가 여러 대인데 각자 풀을 크게 잡으면 그 합이 상한을 넘어 접속 자체가 실패합니다.

그리고 커넥션이 많다고 DB가 더 빨리 처리하지는 않습니다. CPU와 디스크는 정해져 있어서, 동시에 밀어 넣는 요청이 많아지면 서로 경합하며 전체 처리량이 오히려 떨어집니다.

DB 쪽 상한과 현재 사용량 확인sql
-- 세션 상한
SHOW PARAMETER sessions
SHOW PARAMETER processes

-- 현재 사용량과 최고치
SELECT resource_name,
       current_utilization,
       max_utilization,
       limit_value
FROM   v$resource_limit
WHERE  resource_name IN ('processes', 'sessions');

max_utilization 이 limit_value 에 근접했다면 상한을 늘릴지, 애플리케이션 쪽 풀을 줄일지 판단해야 합니다. 인스턴스 수 × 인스턴스당 풀 크기가 상한 안에 들어오는지 계산하는 것이 먼저입니다.

판단 순서

풀을 키우기 전에 확인합니다. 첫째 INACTIVE 세션이 트랜잭션을 붙들고 있는가, 그렇다면 코드 문제입니다. 둘째 ACTIVE 세션이 느린 쿼리를 돌고 있는가, 그렇다면 튜닝 문제입니다. 셋째 둘 다 아니고 실제로 동시 요청이 늘어난 것인가, 그때만 풀 크기를 조정합니다. 조정할 때도 DB 세션 상한을 함께 계산합니다.

04

긴 트랜잭션은 DB에 흔적을 남깁니다

트랜잭션을 짧게 유지하라는 말은 트랙 C에서 이미 했습니다. 여기서는 그 말을 지키지 않았을 때 DB에 무엇이 쌓이는지를 봅니다. 증상으로 원인을 되짚을 수 있어야 하기 때문입니다.

UNDO 증가
누적

트랜잭션이 살아 있는 동안 변경 전 이미지를 계속 보관해야 합니다. 긴 트랜잭션이 많으면 UNDO 공간이 부족해집니다.

ORA-01555 유발
다른 세션 피해

UNDO가 부족해 덮어쓰이면, 오래 도는 조회 쪽에서 snapshot too old 가 납니다. 원인 세션과 피해 세션이 다르다는 점이 진단을 어렵게 합니다.

락 유지
직접 피해

잡은 행 락이 커밋까지 유지됩니다. Part 11의 blocking_session 조회에 원인 세션으로 잡힙니다.

롤백 시간
복구 지연

긴 트랜잭션을 강제 종료하면 되돌릴 양이 많아 롤백에도 오래 걸립니다. 끊었는데 바로 안 풀리는 이유입니다.

오래된 트랜잭션 찾기sql
SELECT s.sid,
       s.serial#,
       s.username,
       s.status,
       s.module,
       s.machine,
       t.start_time,
       ROUND((SYSDATE - TO_DATE(t.start_time, 'MM/DD/YY HH24:MI:SS'))
             * 24 * 60, 1) AS tx_minutes,
       t.used_ublk         AS undo_blocks
FROM   v$transaction t
JOIN   v$session s ON s.saddr = t.ses_addr
ORDER BY t.start_time;

수십 분 이상 살아 있는 트랜잭션이 상시로 보인다면 설계를 봐야 합니다. 배치라면 커밋 단위를 쪼개고, 온라인이라면 트랜잭션 경계가 너무 넓게 잡혀 있는 것입니다.

05

DB 세션과 애플리케이션을 연결해 둡니다

문제 세션을 찾아도 그것이 어느 서버의 어느 기능인지 모르면 조치할 수 없습니다. 인스턴스가 여러 대면 더 어렵습니다.

Part 14에서 SQL 출처 태깅을 다뤘는데, 세션 단위로도 같은 준비가 필요합니다. 장애가 나기 전에 해 둬야 의미가 있습니다.

  • `V$SESSION` 의 `machine` 과 `program` 으로 어느 서버에서 붙었는지 확인합니다. 컨테이너 환경이면 호스트명이 무의미할 수 있으니 별도 표시가 필요합니다.
  • JDBC 연결 속성으로 애플리케이션 이름을 지정해 두면 `program` 에 의미 있는 값이 들어옵니다.
  • `module` 과 `action` 은 Part 14의 `DBMS_APPLICATION_INFO` 로 채웁니다. 배치 잡 이름이나 화면 이름을 넣습니다.
  • 커넥션 풀을 쓰므로 반납 전에 초기화해야 다음 사용자의 정보와 섞이지 않습니다.
  • 조회 전용 작업은 트랜잭션을 읽기 전용으로 분리하면, 불필요한 트랜잭션 유지가 줄고 DB 쪽 부담도 줄어듭니다.
코스 마무리

여기까지가 DB·SQL 코스 전체입니다. 정확한 SQL을 쓰는 것에서 시작해, 빠르게 만들고, 동시 접근을 견디게 하고, 운영에서 진단하고, 애플리케이션과 연결해 추적하는 순서였습니다. 다섯 트랙이 하나의 흐름으로 이어집니다. 애플리케이션 쪽 작성 방법은 Spring Boot 코스에서, 폐쇄망에서의 서버 진단은 터미널·서버 코스에서 이어 볼 수 있습니다.

체크

이 파트 완료 기준