Oracle Lock Session이란? 발생 원인과 대처, Wait Event 확인 방법
Oracle에서 “lock session”은 보통 다른 세션이 잡고 있는 락(또는 래치/뮤텍스/엔큐) 때문에 내 세션이 WAITING 상태로 오래 머무는 상황을 의미합니다. 운영 환경에서는 “느려졌다”로 시작해 결국 Blocking Session(차단 세션)을 찾는 흐름으로 이어지는 경우가 많습니다.
Lock Session의 핵심 개념
- 락을 잡고 있는 세션 = Blocker(Blocking Session)
- 락 때문에 기다리는 세션 = Waiter(Waiting Session)
- 대부분의 “락 대기”는 TX(로우 락) 또는 TM(테이블 락)로 대표됩니다.
Oracle은 동시성을 위해 다양한 동기화 메커니즘을 씁니다. 현장에서 “lock session”이라고 부를 때는 보통 아래 중 하나를 의미합니다.
- enq: TX - row lock contention: 같은 로우(또는 FK 관련) 갱신/삭제 충돌로 대기
- enq: TM - contention: 테이블 레벨의 DML 락(주로 FK 미인덱스/DDL과 충돌 등)
- library cache lock / pin: SQL/PLSQL 객체 컴파일/파싱 등 라이브러리 캐시 경합
- buffer busy waits: 핫 블록(ITL/프리스페이스 등) 경합
- RAC 환경: gc buffer busy, gc current block busy 등 글로벌 캐시 관련 대기
실무 기준으로 보면 “lock session”을 해결하는 첫 단추는 waiter만 보지 말고 blocker를 먼저 특정하는 것입니다.
Lock Session 발생 시 대처 방법
대응은 “진단 → 안전 확인 → 조치” 순서로 가는 게 사고를 줄입니다. 특히 무작정 kill session은 롤백 부담과 2차 장애를 부를 수 있어 절차가 중요합니다.
1) 차단 세션(Blocker)부터 찾기
가장 먼저 waiter와 blocker를 매핑합니다(11g+ 기준으로 단순/명확).
-- Waiter/Blocker 빠르게 보기
SELECT
s.sid,
s.serial#,
s.username,
s.status,
s.event,
s.seconds_in_wait,
s.blocking_session AS blocker_sid,
s.sql_id,
s.machine,
s.program
FROM v$session s
WHERE s.blocking_session IS NOT NULL
ORDER BY s.seconds_in_wait DESC;
Blocker 세션의 SQL과 트랜잭션 상태를 확인합니다.
-- Blocker의 현재/직전 SQL 확인(간단 버전)
SELECT
s.sid, s.serial#, s.username, s.status,
s.sql_id, s.prev_sql_id,
s.event, s.state,
s.machine, s.program
FROM v$session s
WHERE s.sid = :blocker_sid;
-- SQL 텍스트 확인
SELECT sql_id, child_number, sql_text
FROM v$sql
WHERE sql_id = (SELECT sql_id FROM v$session WHERE sid = :blocker_sid);
2) “진짜 락”인지, “다른 대기”인지 구분
event가 TX/TM 계열이면 락 가능성이 높고, library cache / buffer busy / gc 계열이면 원인이 달라집니다. 아래는 세션 대기 상세(파라미터 p1/p2/p3 포함)를 보는 기본 쿼리입니다.
-- 세션의 현재 대기 상세
SELECT
sw.sid,
sw.event,
sw.state,
sw.wait_class,
sw.seconds_in_wait,
sw.p1text, sw.p1,
sw.p2text, sw.p2,
sw.p3text, sw.p3
FROM v$session_wait sw
WHERE sw.sid = :waiter_sid;
3) 조치 옵션(상황별 우선순위)
1) 앱/배치가 정상 종료 가능한지 먼저 확인(커밋/롤백 유도)
2) 장기 트랜잭션이면 영향 범위(롤백 시간/언두/락 범위) 계산
3) 불가피할 때만 kill session(즉시/강제 종료는 마지막 카드)
A. 애플리케이션 레벨에서 해소(가장 안전)
- 문제 트랜잭션을 커밋/롤백하도록 유도
- 배치/스케줄러라면 해당 작업을 중지하고 정상 종료
- 사용자 화면/프로세스가 멈춘 경우 연결 상태(네트워크/세션 유실) 확인
B. DB 레벨에서 세션 종료
세션 종료 전 아래를 확인합니다. (1) blocker가 정말 문제의 원인인지, (2) 롤백 부담이 큰지, (3) 업무 영향이 어디까지인지.
-- 일반 종료(권장)
ALTER SYSTEM KILL SESSION ':sid,:serial' IMMEDIATE;
-- RAC면 인스턴스 포함
ALTER SYSTEM KILL SESSION ':sid,:serial,@:inst_id' IMMEDIATE;
IMMEDIATE는 “바로 사라진다”가 아니라 세션을 종료 요청하고, 남은 작업은 롤백이 진행될 수 있습니다. 대형 트랜잭션이면 “kill 이후 롤백 때문에 더 느려지는” 현상도 가능합니다.
C. 재발 방지(개발/설계 관점)
- 자주 갱신되는 테이블은 짧은 트랜잭션으로 쪼개기
- 동시 업데이트 충돌이 잦으면 업데이트 순서/키 설계 통일
- 필요 시 NOWAIT / SKIP LOCKED 활용
- FK 관련 TM 대기가 있으면 FK 인덱스 점검(미인덱스 FK는 대표적인 락 확대 원인)
- DML과 DDL 동시 수행 최소화(배포/인덱스 작업 시간대 분리)
-- 락을 기다리지 않게(즉시 실패)
SELECT * FROM your_table WHERE ... FOR UPDATE NOWAIT;
-- 락 걸린 로우는 건너뛰고 처리(큐/배치에 유용)
SELECT * FROM your_table WHERE ... FOR UPDATE SKIP LOCKED;
Lock Session 확장: Wait Event 설명 및 확인하는 방법
“확장 확인”의 핵심은 현재(실시간) + 과거(구간)를 함께 보는 것입니다. 실시간은 v$session_wait, 과거 구간은 ASH(Active Session History)가 가장 실무적입니다.
1) 대표 Wait Event 해석(락/경합 중심)
로우 레벨 DML 충돌이 대표적입니다. 같은 로우 갱신, 또는 FK 제약 관련 충돌(특히 부모/자식 삭제/갱신 흐름)에서 자주 나옵니다.
테이블 락 경합. FK 미인덱스, DDL과 DML 충돌, 특정 DML이 테이블 레벨 락을 강하게 잡는 경우에 주의합니다.
파싱/컴파일/객체 재정의 등으로 공유 풀에서 경합이 생길 때. “락”처럼 보이지만 원인은 SQL 공유/DDL/컴파일일 수 있습니다.
핫 블록 경합(ITL 부족, 프리스페이스, 세그먼트/인덱스 특정 블록 집중). 인덱스/테이블 설계와 트랜잭션 패턴을 같이 봐야 합니다.
2) 실시간 확인: 어떤 이벤트로 얼마나 기다리나
-- 특정 세션이 무엇을 기다리는지(실시간)
SELECT
s.sid, s.serial#, s.username,
s.status, s.state,
s.event, s.wait_class,
s.seconds_in_wait,
s.blocking_session,
s.sql_id,
s.machine, s.program
FROM v$session s
WHERE s.sid = :sid;
-- 현재 wait 파라미터(p1/p2/p3)까지
SELECT
sw.sid, sw.event, sw.state, sw.wait_class,
sw.seconds_in_wait,
sw.p1text, sw.p1,
sw.p2text, sw.p2,
sw.p3text, sw.p3
FROM v$session_wait sw
WHERE sw.sid = :sid;
3) 확장 확인(구간 분석): ASH로 “언제부터, 어떤 SQL이, 누구에게 막혔나”
ASH 라이선스/설정에 따라 사용 가능 여부가 다를 수 있지만, 가능하다면 가장 빠르게 그림이 나옵니다.
-- 최근 N분 동안 세션 활동(대기 이벤트/차단자/SQL)
SELECT
sample_time,
session_id AS sid,
session_serial# AS serial#,
sql_id,
event,
wait_class,
blocking_session,
blocking_session_serial#,
time_waited/1000000 AS time_waited_sec
FROM v$active_session_history
WHERE sample_time >= SYSDATE - (10/1440) -- 최근 10분
AND session_id = :sid
ORDER BY sample_time;
-- 최근 N분 동안 "락/엔큐" 위주로 상위 이벤트 집계
SELECT
event,
COUNT(*) AS samples
FROM v$active_session_history
WHERE sample_time >= SYSDATE - (10/1440)
AND event LIKE 'enq:%'
GROUP BY event
ORDER BY samples DESC;
4) 차단 세션을 객체 단위로 좁히기(락 객체/테이블 추적)
락은 결국 “어떤 객체(테이블/인덱스/로우)”에 걸렸는지로 좁혀야 재발 방지가 됩니다.
-- 세션이 잡고 있거나 기다리는 락 확인(객체까지 매핑)
SELECT
s.sid,
s.serial#,
s.username,
l.type,
l.lmode,
l.request,
l.block,
o.owner,
o.object_name,
o.object_type
FROM v$session s
JOIN v$lock l
ON l.sid = s.sid
LEFT JOIN dba_objects o
ON o.object_id = l.id1
WHERE s.sid IN (:waiter_sid, :blocker_sid)
ORDER BY s.sid, l.type;
-- blocker/waiter 관계를 락 키 기준으로 매핑(고급)
SELECT
w.sid AS waiter_sid,
b.sid AS blocker_sid,
w.type,
w.id1, w.id2
FROM v$lock w
JOIN v$lock b
ON w.type = b.type
AND w.id1 = b.id1
AND w.id2 = b.id2
WHERE w.request > 0 -- 기다리는 쪽
AND b.block = 1; -- 막는 쪽
TX/TM 계열은 id1/id2 해석이 필요할 수 있고 버전에 따라 접근 방식이 달라집니다. 하지만 현업에서는 먼저 blocking_session + sql_id + dba_objects 조합으로도 대부분 방향이 잡힙니다.
현장 체크리스트
1) v$session에서 blocking_session 확인 → blocker SID 확보
2) blocker의 sql_id / 프로그램 / 사용자 / 머신 확인
3) event가 TX/TM인지, library/buffer/gc인지 구분
4) 업무 영향/롤백 부담 확인 후 정상 종료 유도 → 최후에 kill session
5) 원인 패턴(긴 트랜잭션, FK 인덱스, DDL 동시 수행, 핫 블록)을 찾아 재발 방지 반영
실제 사용 시에는 “대기 세션만” 보고 조급해지기 쉽지만, 관리자 입장에서는 차단 세션의 정체와 작업 내용이 먼저 정리되어야 안전한 조치가 가능합니다.
'지식 공유 > DBMS' 카테고리의 다른 글
| TNS-12543 / TNS-00513 오류: 호스트 연결 실패 (0) | 2026.01.25 |
|---|---|
| ORA-16055 FAL request rejected 오류: Data Guard 갭 복구 실패 (0) | 2026.01.25 |
| [ORACLE] CSScan DMU TDE (0) | 2025.12.27 |
| [ORACLE] PLS-00306 조치 절차 (0) | 2025.12.24 |
| [PostgreSQL] PANIC 체크포인트 레코드 오류 (0) | 2025.12.21 |
