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;
예방 방법
ORA 에러를 번호 순서대로 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
⚠ 본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.