2026년 08월 19일 | DBMS Error 가이드
이 글에서 다루는 내용
ORA-04020 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
ORA-04020 deadlock detected while trying to lock object 는?
ORA-04020 에러는 Oracle 데이터베이스에서 두 개 이상의 세션이 서로 상대방이 보유한 객체 잠금(Object Lock)을 획득하려고 시도할 때 발생하는 교착 상태(Deadlock) 에러입니다. 이 에러는 주로 DDL 작업(테이블 컴파일, 패키지 재컴파일, 뷰 변경 등) 중에 발생하며, 일반적인 DML 레벨 교착 상태와는 다르게 라이브러리 캐시(Library Cache) 또는 딕셔너리 캐시(Dictionary Cache) 레벨에서의 잠금 충돌을 의미합니다. 운영 환경에서 배포 작업이나 스키마 변경 작업 중 여러 세션이 동시에 동일한 객체에 접근할 경우 빈번하게 나타날 수 있습니다.
주요 발생 원인
1. 동시 DDL 작업으로 인한 객체 잠금 충돌
두 개 이상의 세션이 동시에 같은 데이터베이스 객체(패키지, 프로시저, 트리거, 뷰 등)에 대해 컴파일 또는 변경 작업을 수행할 때 가장 많이 발생합니다. 예를 들어 세션 A가 패키지 BODY를 컴파일하는 동안, 세션 B가 해당 패키지가 참조하는 다른 객체를 변경하려 할 때 상호 대기 상태에 빠지게 됩니다. 배포 자동화 스크립트가 병렬로 실행되거나, 여러 DBA가 동시에 작업할 때 주로 목격되는 패턴입니다.
2. 무효화된 객체(INVALID Object)의 자동 재컴파일 충돌
Oracle은 의존성이 있는 객체가 변경되면 관련된 객체들을 자동으로 INVALID 상태로 만들고, 다음 호출 시 자동 재컴파일을 시도합니다. 여러 세션이 동시에 INVALID 상태인 같은 객체를 호출하면, 각 세션이 재컴파일 잠금을 획득하려는 과정에서 순환 대기가 발생할 수 있습니다. 특히 대형 패키지 계층 구조를 가진 시스템에서 배포 직후 트래픽이 유입될 때 집중적으로 발생합니다.
3. 분산 트랜잭션(Distributed Transaction) 또는 DB Link 사용 시 잠금 전파
DB Link를 통한 원격 데이터베이스 객체 접근 시, 로컬과 원격 양쪽에서 잠금이 발생하여 교착 상태로 이어질 수 있습니다. 특히 양방향 DB Link가 설정된 환경에서 양쪽 데이터베이스가 서로의 객체를 참조하는 트랜잭션을 동시에 실행할 경우, 잠금 체인이 네트워크를 넘어 형성되어 ORA-04020이 발생합니다. 이 경우 에러 감지 자체도 지연될 수 있어 원인 파악이 어렵습니다.
해결 방법
원인 1 해결: 동시 DDL 충돌 해소
현재 객체를 잠금 중인 세션을 조회하고, 필요시 강제 종료합니다.
-- 현재 라이브러리 캐시 잠금 현황 조회
SELECT
s.sid,
s.serial#,
s.username,
s.status,
s.machine,
lo.object AS locked_object,
lo.type,
lo.mode_held,
lo.mode_requested
FROM
v$session s,
v$library_cache_lock lo
WHERE
s.saddr = lo.session_id
ORDER BY
s.sid;
-- 특정 객체를 잠금 중인 세션 확인
SELECT
s.sid,
s.serial#,
s.username,
s.program,
do.object_name,
do.object_type,
lo.mode_held,
lo.mode_requested
FROM
v$session s
JOIN v$library_cache_lock lo ON s.saddr = lo.session_id
JOIN dba_objects do ON lo.object = do.object_id
WHERE
do.object_name = 'YOUR_OBJECT_NAME' -- 대상 객체명 입력
ORDER BY
lo.mode_held DESC;
-- 잠금을 유발하는 세션 강제 종료 (주의: 운영 환경에서 신중히 사용)
ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;
-- 예시: ALTER SYSTEM KILL SESSION '142,3891' IMMEDIATE;
원인 2 해결: INVALID 객체 사전 재컴파일
배포 후 트래픽 유입 전에 INVALID 객체를 수동으로 재컴파일하여 자동 재컴파일 충돌을 예방합니다.
-- INVALID 상태 객체 목록 조회
SELECT
owner,
object_name,
object_type,
status,
last_ddl_time
FROM
dba_objects
WHERE
status = 'INVALID'
AND owner NOT IN ('SYS', 'SYSTEM', 'OUTLN', 'DBSNMP')
ORDER BY
object_type,
object_name;
-- UTL_RECOMP 패키지를 사용한 스키마 내 일괄 재컴파일 (직렬 방식 권장)
BEGIN
UTL_RECOMP.recomp_serial(schema => 'YOUR_SCHEMA_NAME');
END;
/
-- 병렬 재컴파일 (CPU 코어 수에 맞게 조정, 단 교착 위험 있으므로 주의)
BEGIN
UTL_RECOMP.recomp_parallel(
threads => 4,
schema => 'YOUR_SCHEMA_NAME'
);
END;
/
-- 특정 객체 수동 재컴파일
ALTER PACKAGE your_schema.your_package COMPILE;
ALTER PACKAGE your_schema.your_package COMPILE BODY;
ALTER PROCEDURE your_schema.your_procedure COMPILE;
ALTER VIEW your_schema.your_view COMPILE;
-- 스크립트로 전체 INVALID 객체 재컴파일 SQL 생성
SELECT
'ALTER ' || object_type || ' ' || owner || '.' || object_name || ' COMPILE;' AS recompile_sql
FROM
dba_objects
WHERE
status = 'INVALID'
AND object_type IN ('PACKAGE', 'PACKAGE BODY', 'PROCEDURE', 'FUNCTION', 'TRIGGER', 'VIEW')
AND owner = 'YOUR_SCHEMA_NAME'
ORDER BY
CASE object_type
WHEN 'PACKAGE' THEN 1
WHEN 'PACKAGE BODY' THEN 2
WHEN 'PROCEDURE' THEN 3
WHEN 'FUNCTION' THEN 4
WHEN 'TRIGGER' THEN 5
WHEN 'VIEW' THEN 6
END;
원인 3 해결: DB Link 관련 잠금 해소
-- 현재 활성 분산 트랜잭션 확인
SELECT
d.local_tran_id,
d.global_tran_id,
d.state,
d.mixed,
d.host,
d.db_link,
s.sid,
s.username
FROM
dba_2pc_pending d
LEFT JOIN v$session s ON d.local_tran_id = s.taddr;
-- 고아 분산 트랜잭션 강제 롤백 (DBA 권한 필요)
ROLLBACK FORCE 'local_tran_id_value';
-- 예시: ROLLBACK FORCE '1.21.3457';
-- DB Link 세션 현황 확인
SELECT
s.sid,
s.serial#,
s.username,
s.server,
s.osuser,
s.program,
s.sql_id
FROM
v$session s
WHERE
s.server = 'PSEUDO' -- DB Link 사용 세션 식별
ORDER BY
s.sid;
예방 방법
1. 단일 배포 창(Deployment Window) 운영 및 순차적 DDL 실행
운영 환경에서 DDL 작업은 반드시 정해진 배포 창 내에서, 단일 세션으로 순차적으로 수행해야 합니다. 배포 스크립트에서 각 DDL 문 사이에 충분한 대기 시간(DBMS_LOCK.SLEEP)을 부여하고, 배포 완료 후 UTL_RECOMP.recomp_serial()을 실행하여 모든 의존 객체를 재컴파일한 뒤 서비스 트래픽을 전환하는 절차를 표준화하는 것이 핵심입니다.
-- 배포 후 재컴파일 및 검증 표준 스크립트 예시
BEGIN
-- 1단계: 직렬 재컴파일 수행
UTL_RECOMP.recomp_serial(schema => 'APP_SCHEMA');
-- 2단계: 재컴파일 후 INVALID 객체 잔존 여부 확인
FOR rec IN (
SELECT object_name, object_type
FROM dba_objects
WHERE status = 'INVALID'
AND owner = 'APP_SCHEMA'
) LOOP
DBMS_OUTPUT.PUT_LINE('STILL INVALID: ' || rec.object_type || ' - ' || rec.object_name);
END LOOP;
END;
/
2. DDL_LOCK_TIMEOUT 파라미터 설정으로 무한 대기 방지
DDL_LOCK_TIMEOUT 파라미터를 적절히 설정하면, DDL 작업이 잠금 획득에 실패할 경우 무한정 대기하지 않고 지정된 시간 후 에러를 반환하게 할 수 있습니다. 이를 통해 교착 상태가 장기간 지속되는 상황을 방지하고, 운영팀이 신속하게 상황을 인지하고 대응할 수 있습니다.
-- 세션 레벨 DDL 잠금 타임아웃 설정 (초 단위, 0 = 즉시 실패, 기본값은 무제한 대기)
ALTER SESSION SET DDL_LOCK_TIMEOUT = 30;
-- 시스템 레벨 설정 (모든 세션에 적용)
ALTER SYSTEM SET DDL_LOCK_TIMEOUT = 30 SCOPE = BOTH;
-- 현재 파라미터 값 확인
SELECT name, value, description
FROM v$parameter
WHERE name = 'ddl_lock_timeout';
관련 에러
- ORA-00060: 일반 DML 레벨 교착 상태(Deadlock). ORA-04020이 DDL/라이브러리 캐시 레벨 교착 상태라면, ORA-00060은 행(Row) 레벨 잠금에서의 교착 상태입니다.
- ORA-04021:
timeout occurred while waiting to lock object. ORA-04020과 유사하나, 교착 상태 대신 설정된 타임아웃 시간 초과로 발생합니다. - ORA-00604:
error occurred at recursive SQL level. ORA-04020 발생 시 함께 나타나는 경우가 많으며, Oracle 내부 재귀 SQL 수행 중 에러를 의미합니다. - ORA-02049:
timeout: distributed transaction waiting for lock. DB Link 환경의 분산 트랜잭션에서 잠금 대기 타임아웃 시 발생하며, ORA-04020의 분산 트랜잭션 케이스와 연관됩니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.