ORA-00051 오류 원인과 해결 방법 완벽 가이드

ORA-00051
2026년 05월 22일 | Oracle DBA 가이드

📌 이 글에서 다루는 내용

ORA-00051 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.

# ORA-00051: timeout occurred while waiting for a resource

ORA-00051란?

ORA-00051은 Oracle 데이터베이스에서 특정 리소스를 기다리는 도중 설정된 타임아웃 시간이 초과되었을 때 발생하는 에러입니다. 주로 분산 트랜잭션(Distributed Transaction) 환경에서 원격 데이터베이스와의 통신 중 락(Lock)이나 리소스 할당을 기다리다가 응답이 없을 때 나타납니다. 이 에러는 단순한 네트워크 지연부터 데드락(Deadlock), 잘못된 DB Link 설정, 과부하 상태의 시스템에 이르기까지 다양한 원인으로 인해 트리거될 수 있기 때문에, 발생 원인을 정확히 파악하고 대응하는 것이 중요합니다.


주요 발생 원인

1. 분산 트랜잭션(DB Link)에서의 락 대기 타임아웃

Oracle DB Link를 통해 원격 데이터베이스에 접근할 때, 원격 측에서 특정 리소스(테이블, 로우 등)에 대한 락을 보유한 세션이 있으면 현재 세션은 대기 상태에 들어갑니다. 이 대기 시간이 DISTRIBUTED_LOCK_TIMEOUT 파라미터에 설정된 값을 초과하면 ORA-00051이 발생합니다.

2. DISTRIBUTED_LOCK_TIMEOUT 파라미터 값이 너무 낮게 설정된 경우

DISTRIBUTED_LOCK_TIMEOUT 초기화 파라미터는 분산 트랜잭션에서 락을 기다리는 최대 시간을 초(second) 단위로 지정합니다. 기본값은 60초이며, 네트워크 지연이 잦거나 복잡한 분산 처리를 수행하는 환경에서는 이 값이 너무 낮게 설정되어 정상적인 작업도 타임아웃으로 종료될 수 있습니다.

3. 네트워크 불안정 및 원격 DB 응답 지연

로컬 DB와 원격 DB 사이의 네트워크 패킷 손실, 지연 또는 방화벽 세션 만료 등으로 인해 원격 DB로부터 응답이 지연될 수 있습니다. 이 경우 Oracle은 리소스를 할당받지 못한 상태에서 타임아웃을 발생시켜 ORA-00051을 반환합니다.

4. 장기 실행 트랜잭션으로 인한 락 충돌

원격 DB 혹은 로컬 DB의 특정 세션이 커밋(Commit)이나 롤백(Rollback)을 하지 않은 채 락을 오랫동안 유지하는 경우, 다른 세션의 대기가 길어지고 타임아웃으로 이어질 수 있습니다. 특히 배치 프로그램이나 장기 DML 작업 이후 명시적으로 트랜잭션을 종료하지 않은 상황에서 자주 발생합니다.

5. In-doubt 트랜잭션(분산 트랜잭션 불완전 종료)

분산 트랜잭션 도중 네트워크 단절이나 원격 DB 장애로 인해 트랜잭션이 완전히 커밋 또는 롤백되지 못하고 “In-doubt” 상태로 남는 경우가 있습니다. 이런 In-doubt 트랜잭션은 관련 리소스에 대한 락을 계속 보유하여 이후 동일 리소스에 접근하는 세션에서 ORA-00051을 유발할 수 있습니다.


해결 방법

1. DISTRIBUTED_LOCK_TIMEOUT 파라미터 확인 및 조정

먼저 현재 설정값을 확인하고, 업무 특성에 맞게 적절한 값으로 조정합니다.

-- 현재 DISTRIBUTED_LOCK_TIMEOUT 값 확인
SHOW PARAMETER DISTRIBUTED_LOCK_TIMEOUT;

-- 또는 V$PARAMETER 뷰에서 확인
SELECT NAME, VALUE, DESCRIPTION
FROM   V$PARAMETER
WHERE  NAME = 'distributed_lock_timeout';
-- 시스템 레벨에서 타임아웃 값 변경 (예: 120초로 증가)
-- 동적 파라미터이므로 재시작 없이 적용 가능
ALTER SYSTEM SET DISTRIBUTED_LOCK_TIMEOUT = 120;

-- 변경 후 확인
SELECT NAME, VALUE
FROM   V$PARAMETER
WHERE  NAME = 'distributed_lock_timeout';

> ⚠️ 값을 무작정 높이는 것은 근본적인 해결책이 아닙니다. 반드시 원인 분석과 병행하여 조정하세요.


2. 현재 락 대기 세션 및 블로킹 세션 확인

-- 락 대기 중인 세션 조회
SELECT
    s.SID,
    s.SERIAL#,
    s.USERNAME,
    s.STATUS,
    s.WAIT_CLASS,
    s.EVENT,
    s.SECONDS_IN_WAIT,
    s.SQL_ID,
    s.MACHINE,
    s.PROGRAM
FROM
    V$SESSION s
WHERE
    s.WAIT_CLASS != 'Idle'
    AND s.STATUS = 'ACTIVE'
ORDER BY
    s.SECONDS_IN_WAIT DESC;
-- 블로킹 세션과 대기 세션 간의 관계 파악
SELECT
    DECODE(request, 0, 'Holder: ', 'Waiter: ') || s.SID AS SID_INFO,
    s.USERNAME,
    s.MACHINE,
    s.PROGRAM,
    l.TYPE,
    l.ID1,
    l.ID2,
    l.LMODE,
    l.REQUEST,
    l.CTIME,
    s.SQL_ID
FROM
    V$LOCK l
    JOIN V$SESSION s ON l.SID = s.SID
WHERE
    (l.ID1, l.ID2, l.TYPE) IN (
        SELECT ID1, ID2, TYPE
        FROM   V$LOCK
        WHERE  REQUEST > 0
    )
ORDER BY
    l.ID1, l.ID2, l.REQUEST;

3. 블로킹 세션 강제 종료

원인 세션을 파악한 후, 업무 영향도를 검토하고 필요한 경우 강제 종료합니다.

-- 특정 세션 강제 종료 (SID와 SERIAL# 확인 후 실행)
ALTER SYSTEM KILL SESSION '블로킹_SID, 블로킹_SERIAL#' IMMEDIATE;

-- 예시
ALTER SYSTEM KILL SESSION '145, 3821' IMMEDIATE;

-- 세션 종료 후 상태 확인
SELECT SID, SERIAL#, STATUS, USERNAME
FROM   V$SESSION
WHERE  SID = 145;

4. In-doubt 트랜잭션 확인 및 처리

-- In-doubt 분산 트랜잭션 조회
SELECT
    LOCAL_TRAN_ID,
    GLOBAL_TRAN_ID,
    STATE,
    MIXED,
    ADVICE,
    TRAN_COMMENT,
    FAIL_TIME,
    FORCE_TIME
FROM
    DBA_2PC_PENDING;
-- In-doubt 트랜잭션을 강제로 커밋 처리 (DBA 판단 후 실행)
-- STATE가 'prepared'인 경우, 원격 DB와 협의 후 결정
COMMIT FORCE 'LOCAL_TRAN_ID 값';

-- 예시
COMMIT FORCE '1.35.12';

-- 또는 강제 롤백
ROLLBACK FORCE '1.35.12';
-- 처리 후 DBA_2PC_PENDING에서 제거 확인
SELECT COUNT(*)
FROM   DBA_2PC_PENDING;

-- DBMS_TRANSACTION 패키지를 사용한 정리 (Oracle 권장)
EXEC DBMS_TRANSACTION.PURGE_LOST_DB_ENTRY('LOCAL_TRAN_ID 값');

5. DB Link 연결 상태 확인

-- 현재 사용 중인 DB Link 목록 확인
SELECT DB_LINK, USERNAME, HOST, CREATED
FROM   DBA_DB_LINKS;

-- 활성화된 DB Link 세션 확인
SELECT
    s.SID,
    s.SERIAL#,
    s.USERNAME,
    s.STATUS,
    d.DB_LINK,
    d.HETEROGENEOUS,
    d.PROTOCOL,
    d.OPEN_CURSORS
FROM
    V$SESSION s
    JOIN V$DBLINK d ON s.SID = d.SID;

-- DB Link 연결 테스트
SELECT * FROM DUAL@원격_DB_LINK명;

6. 장기 미완료 트랜잭션 확인

-- 오래된 활성 트랜잭션 확인 (1시간 이상)
SELECT
    s.SID,
    s.SERIAL#,
    s.USERNAME,
    s.STATUS,
    t.START_TIME,
    ROUND((SYSDATE - TO_DATE(t.START_TIME, 'MM/DD/YY HH24:MI:SS')) * 24 * 60, 2) AS ELAPSED_MIN,
    t.USED_UBLK,
    t.LOG_IO,
    s.SQL_ID
FROM
    V$TRANSACTION t
    JOIN V$SESSION s ON t.SES_ADDR = s.SADDR
WHERE
    (SYSDATE - TO_DATE(t.START_TIME, 'MM/DD/YY HH24:MI:SS')) * 24 * 60 > 60
ORDER BY
    ELAPSED_MIN DESC;

예방 방법

📚 Oracle 에러 코드 시리즈

ORA 에러를 번호 순서대로 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.

⚠ 본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.

댓글 남기기