DB·SQL 실무 가이드 · Part 11

락과 데드락

화면이 멈췄을 때 원인 세션을 찾아 끊어내기

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

이 파트에서 다루는 내용

락의 종류대기 세션 추적데드락배치와 온라인 충돌DDL 락
01

Oracle의 락은 행 단위이고 승격되지 않습니다

Oracle은 수정하는 행에만 락을 겁니다. 그리고 행 락이 많아졌다고 해서 테이블 락으로 바꾸지 않습니다.

MSSQL에는 락이 일정 수를 넘으면 테이블 락으로 승격하는 동작이 있어서, 대량 수정이 갑자기 테이블 전체를 막는 일이 생깁니다. Oracle에는 그런 승격이 없습니다. 이관 시 동시성 특성이 달라지는 지점입니다.

다만 잠금은 커밋이나 롤백까지 유지되고 기본적으로 시간 제한이 없습니다. 즉 누군가 커밋하지 않으면 상대는 무한정 기다립니다.

TX 락
행 락

DML이 수정하는 행에 걸립니다. 같은 행을 고치려는 다른 세션만 대기합니다.

TM 락
테이블 락

DML 중에 그 테이블의 구조가 바뀌지 않도록 거는 약한 락입니다. 다른 DML은 막지 않고 DDL만 막습니다.

락 승격 없음
Oracle 특징

행 락이 아무리 많아져도 테이블 락이 되지 않습니다. 락 정보를 데이터 블록에 함께 두는 구조라 개수 제한이 없습니다.

무한 대기
주의

기본적으로 타임아웃이 없습니다. 대기 세션이 스스로 포기하게 하려면 FOR UPDATE WAIT n 이나 애플리케이션 타임아웃이 필요합니다.

02

누가 누구를 막고 있는지 찾습니다

화면이 멈췄다는 신고가 들어오면 가장 먼저 확인할 것입니다. Oracle은 대기 세션이 어떤 세션 때문에 막혀 있는지 직접 알려 줍니다.

대기 관계 조회sql
SELECT s.sid,
       s.serial#,
       s.username,
       s.status,
       s.event,                      -- enq: TX - row lock contention
       s.seconds_in_wait,
       s.blocking_session AS blocker_sid,
       s.machine,
       s.program
FROM   v$session s
WHERE  s.blocking_session IS NOT NULL
ORDER BY s.seconds_in_wait DESC;

blocking_session 이 원인 세션의 SID입니다. event 가 enq: TX - row lock contention 이면 행 락 대기입니다. seconds_in_wait 으로 얼마나 오래 막혔는지 봅니다.

원인 세션이 무엇을 하고 있는지sql
SELECT s.sid, s.serial#, s.username, s.status,
       s.last_call_et AS idle_sec,   -- 마지막 작업 이후 경과 시간
       s.machine, s.program,
       q.sql_text
FROM   v$session s
LEFT JOIN v$sql q ON q.sql_id = s.prev_sql_id
WHERE  s.sid = :blocker_sid;

status 가 INACTIVE 이고 idle_sec 이 크면 사람이 커밋하지 않고 자리를 비운 경우입니다. ACTIVE 면 실제로 긴 작업이 도는 중이므로 끊기 전에 담당자를 먼저 확인합니다.

세션 종료sql
-- 확인 후에만 실행합니다
ALTER SYSTEM KILL SESSION '123,45678' IMMEDIATE;

끊으면 그 세션의 트랜잭션이 롤백됩니다. 되돌릴 양이 많으면 롤백에도 시간이 걸립니다. 무엇을 하던 세션인지 확인하지 않고 끊으면 업무 데이터가 통째로 사라질 수 있으므로, 운영에서는 담당자 확인을 원칙으로 합니다.

03

데드락은 잠그는 순서가 엇갈릴 때 생깁니다

A 세션이 1번 행을 잡고 2번 행을 기다리는데, B 세션이 2번 행을 잡고 1번 행을 기다리면 둘 다 영원히 못 나갑니다.

Oracle은 이걸 자동으로 감지해 한쪽에 ORA-00060 을 냅니다. 여기서 중요한 것이 있습니다. Oracle은 트랜잭션 전체를 롤백하지 않고 데드락을 유발한 그 문장 하나만 되돌립니다.

그래서 애플리케이션이 이 오류를 잡아 처리하지 않으면, 트랜잭션은 살아 있는데 일부만 실행된 어중간한 상태로 남습니다. 오류를 받으면 명시적으로 롤백하고 재시도해야 합니다.

접근 순서 통일
가장 확실한 예방

여러 행이나 여러 테이블을 다룰 때 항상 같은 순서로 잠급니다. 정렬해서 처리하는 것만으로 대부분 해결됩니다.

트랜잭션 축소
확률 감소

잠그고 있는 시간이 짧을수록 엇갈릴 확률이 줄어듭니다.

인덱스 없는 외래키
숨은 원인

자식 테이블의 FK 컬럼에 인덱스가 없으면, 부모 행을 삭제하거나 키를 수정할 때 Oracle이 자식 테이블에 락을 겁니다. 원인을 찾기 어려운 데드락의 대표적인 출처입니다.

추적 파일
사후 분석

데드락이 발생하면 alert log에 기록되고 트레이스 파일이 남습니다. 어떤 SQL이 엇갈렸는지 여기서 확인합니다.

인덱스 없는 외래키 찾기sql
SELECT c.table_name,
       c.constraint_name,
       cc.column_name
FROM   user_constraints c
JOIN   user_cons_columns cc
  ON   cc.constraint_name = c.constraint_name
WHERE  c.constraint_type = 'R'
AND    NOT EXISTS (
         SELECT 1
         FROM   user_ind_columns ic
         WHERE  ic.table_name  = c.table_name
         AND    ic.column_name = cc.column_name
         AND    ic.column_position = 1
       )
ORDER BY c.table_name;

여기 나온 FK 컬럼에는 인덱스를 만드는 것을 검토합니다. 부모 삭제가 전혀 없는 마스터 테이블이라면 그대로 둬도 되지만, 대부분은 만들어 두는 편이 안전합니다.

04

배치가 온라인 화면을 멈추는 상황

야간 배치가 대량 UPDATE를 한 트랜잭션으로 돌리면 그 행들이 전부 잠깁니다. 배치가 도는 동안 같은 데이터를 쓰는 온라인 업무가 막힙니다.

낮 시간에 대량 정정 작업을 요청받았을 때 특히 위험합니다. 한 번에 실행하면 그 순간 업무가 멈춥니다.

  • 커밋 단위를 쪼갭니다. 수천 건 단위로 나누면 각 구간이 끝날 때마다 락이 풀립니다.
  • 쪼갠 만큼 중간 실패 시 상태가 어중간해지므로, 처리 진행 상태를 기록해 재시작할 수 있게 만듭니다.
  • 작업 큐 방식이면 FOR UPDATE SKIP LOCKED 로 잠긴 건을 건너뛰어 여러 프로세스가 동시에 처리할 수 있습니다.
  • 온라인 업무와 시간대를 분리할 수 있으면 그것이 가장 단순한 해법입니다.
  • TRUNCATE 는 DDL이라 테이블 배타 락을 걸고 되돌릴 수도 없습니다. 운영 중에는 쓰지 않습니다.
커밋 단위를 나눈 대량 갱신sql
DECLARE
  CURSOR c_target IS
    SELECT order_id FROM orders
    WHERE  status_cd = 'READY' AND order_dt < SYSDATE - 30;
  v_cnt NUMBER := 0;
BEGIN
  FOR r IN c_target LOOP
    UPDATE orders SET status_cd = 'EXPIRED' WHERE order_id = r.order_id;
    v_cnt := v_cnt + 1;

    IF MOD(v_cnt, 5000) = 0 THEN
      COMMIT;   -- 5000건마다 락을 풀어 줍니다
    END IF;
  END LOOP;
  COMMIT;
END;
/

커밋 주기가 너무 짧으면 오히려 느려지고, 너무 길면 락을 오래 잡습니다. 수천 건 단위가 일반적인 출발점이고 실제 환경에서 측정해 조정합니다. 중간 커밋을 하는 순간 전체 되돌리기는 포기하는 것이므로, 재실행해도 안전한 조건인지 먼저 확인합니다.

05

운영 중 DDL은 테이블을 멈춥니다

컬럼 추가, 인덱스 생성, 제약조건 변경은 모두 DDL이고 테이블에 배타 락을 요구합니다. 그 순간 해당 테이블을 쓰는 모든 작업이 막힙니다.

게다가 DDL은 락을 얻지 못하면 기본적으로 즉시 실패합니다. ORA-00054 가 나면 다른 세션이 그 테이블을 쓰고 있다는 뜻입니다.

DDL 락 대기 시간 설정sql
-- 기본은 0초. 락을 못 잡으면 즉시 ORA-00054
ALTER SESSION SET ddl_lock_timeout = 30;

-- 인덱스 생성은 ONLINE 옵션으로 DML을 막지 않고 만들 수 있습니다
CREATE INDEX idx_orders_status ON orders (status_cd) ONLINE;

ONLINE 옵션은 DML을 막지 않지만 부하는 그대로 발생하고 시간도 더 걸립니다. 작업 시간대를 잡는 것이 기본이고 ONLINE은 보조 수단입니다.

락 문제 대응 순서

첫째 V$SESSION 에서 blocking_session 으로 원인 세션을 찾습니다. 둘째 그 세션이 INACTIVE 인지 ACTIVE 인지, 어떤 SQL을 실행했는지 확인합니다. 셋째 담당자를 확인해 스스로 커밋 또는 롤백하게 합니다. 넷째 연락이 안 되고 업무 영향이 크면 그때 세션을 끊습니다. 끊는 것이 첫 번째 선택지가 되면 안 됩니다.

체크

이 파트 완료 기준