DEV Community

umzzil nng
umzzil nng

Posted on • Originally published at oraerror.com

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

ORA-00055 maximum number of DML locks exceeded 는?

ORA-00055 에러는 Oracle 데이터베이스에서 DML(Data Manipulation Language) 잠금(Lock)의 최대 허용 개수를 초과했을 때 발생하는 에러입니다. Oracle은 INSERT, UPDATE, DELETE, MERGE 등의 DML 작업 시 해당 테이블에 대한 테이블 수준의 DML 잠금(TM Lock)을 내부적으로 관리하는데, 이 잠금을 저장하는 내부 구조체(Enqueue)의 슬롯이 모두 소진되면 이 에러가 발생합니다. 주로 동시에 수백~수천 개의 세션이 다수의 테이블에 대해 DML을 수행하는 대용량 OLTP 환경이나 배치 처리 환경에서 나타나며, 서비스 중단으로 이어질 수 있어 신속한 조치가 필요합니다.


주요 발생 원인

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

DML_LOCKS 초기화 파라미터는 인스턴스 전체에서 허용되는 DML 잠금의 최대 개수를 결정합니다. 기본값은 4 * TRANSACTIONS 값으로 자동 계산되지만, 트랜잭션 수가 급증하거나 초기 설정 당시 예상보다 훨씬 많은 동시 DML이 발생하는 경우 이 값이 턱없이 부족해질 수 있습니다. 특히 애플리케이션 기능이 추가되거나 사용자 수가 급격히 늘어난 시스템에서 자주 목격됩니다.

2. 하나의 트랜잭션에서 지나치게 많은 테이블을 동시에 조작하는 경우

단일 트랜잭션 내에서 수십~수백 개의 테이블에 순차적으로 DML을 수행하면, 트랜잭션이 완료(COMMIT 또는 ROLLBACK)되기 전까지 해당 테이블 수만큼 DML 잠금이 점유된 채 유지됩니다. 배치 프로그램이나 복잡한 ERP 트랜잭션에서 이런 패턴이 많고, 커밋 없이 장시간 실행되는 배치 작업이 잠금을 독식하는 상황이 발생합니다. 결국 다른 세션이 잠금 슬롯을 할당받지 못해 ORA-00055가 연쇄적으로 발생하게 됩니다.

3. 커밋 없이 장시간 유지되는 미완료 트랜잭션(Long-Running Transaction)

명시적인 COMMIT 없이 오랜 시간 유지되는 트랜잭션은 DML 잠금 슬롯을 계속 점유합니다. 특히 애플리케이션 로직에서 예외 처리가 미흡하거나, 커넥션 풀에서 세션이 트랜잭션을 열어 놓은 채 반환되는 경우, 해당 슬롯이 해제되지 않아 점차 가용 슬롯이 줄어듭니다. 이런 좀비(Zombie) 트랜잭션이 누적될 경우 순간적으로 DML 잠금 한계에 도달할 수 있습니다.


해결 방법

원인 1 해결: DML_LOCKS 파라미터 값 증가

먼저 현재 설정값과 실제 사용량을 확인합니다.

-- 현재 DML_LOCKS 파라미터 값 확인
SHOW PARAMETER DML_LOCKS;

-- 또는
SELECT name, value, description
FROM   v$parameter
WHERE  name = 'dml_locks';
Enter fullscreen mode Exit fullscreen mode
-- 현재 시스템에서 사용 중인 DML Lock(Enqueue) 현황 확인
SELECT inst_id,
       eq_type,
       total_req#,
       total_wait#,
       succ_req#,
       failed_req#
FROM   gv$enqueue_stat
WHERE  eq_type = 'TM'
ORDER BY inst_id;
Enter fullscreen mode Exit fullscreen mode
-- 현재 활성화된 DML Lock 세부 현황
SELECT s.sid,
       s.serial#,
       s.username,
       s.status,
       l.type,
       l.lmode,
       l.request,
       o.object_name,
       o.object_type
FROM   v$lock l
JOIN   v$session s ON l.sid = s.sid
JOIN   dba_objects o ON l.id1 = o.object_id
WHERE  l.type = 'TM'
ORDER BY s.sid;
Enter fullscreen mode Exit fullscreen mode

충분한 여유값을 확인한 후 파라미터를 동적으로 변경합니다. DML_LOCKS동적 파라미터가 아니므로 SPFILE 변경 후 재기동이 필요합니다.

-- SPFILE을 사용하는 경우 (재기동 후 적용)
ALTER SYSTEM SET DML_LOCKS = 10000 SCOPE = SPFILE;

-- 변경 후 DB 재기동
-- SHUTDOWN IMMEDIATE;
-- STARTUP;

-- 변경 확인
SELECT name, value
FROM   v$spparameter
WHERE  name = 'dml_locks';
Enter fullscreen mode Exit fullscreen mode

⚠️ 주의: DML_LOCKS = 0으로 설정하면 DML 잠금 자체를 비활성화할 수 있으나, 이 경우 DROP TABLE, TRUNCATE 등 DDL 작업이 불가능해지므로 운영 환경에서는 절대 권장하지 않습니다.


원인 2 해결: 트랜잭션 범위 최소화 및 배치 분리

하나의 트랜잭션에서 처리하는 테이블 수를 줄이고, 적절한 중간 COMMIT을 삽입합니다.

-- 나쁜 예시: 하나의 트랜잭션에서 수십 개 테이블 처리
BEGIN
    UPDATE table_01 SET col1 = 'A' WHERE condition;
    UPDATE table_02 SET col1 = 'B' WHERE condition;
    -- ... table_03 ~ table_99 반복 ...
    UPDATE table_99 SET col1 = 'Z' WHERE condition;
    COMMIT; -- 모든 DML 잠금이 이 시점까지 점유됨
END;
/

-- 좋은 예시: 논리 단위로 분리하여 중간 커밋
BEGIN
    -- 1단계: 마스터 테이블 처리
    UPDATE table_01 SET col1 = 'A' WHERE condition;
    UPDATE table_02 SET col1 = 'B' WHERE condition;
    COMMIT; -- 잠금 즉시 해제

    -- 2단계: 상세 테이블 처리
    UPDATE table_03 SET col1 = 'C' WHERE condition;
    UPDATE table_04 SET col1 = 'D' WHERE condition;
    COMMIT; -- 잠금 즉시 해제
END;
/
Enter fullscreen mode Exit fullscreen mode
-- 대량 배치 처리 시 BULK COLLECT + FORALL + 중간 커밋 패턴
DECLARE
    TYPE t_id_list IS TABLE OF orders.order_id%TYPE;
    l_ids   t_id_list;
    l_limit NUMBER := 1000; -- 배치 사이즈

    CURSOR c_orders IS
        SELECT order_id FROM orders WHERE status = 'PENDING';
BEGIN
    OPEN c_orders;
    LOOP
        FETCH c_orders BULK COLLECT INTO l_ids LIMIT l_limit;
        EXIT WHEN l_ids.COUNT = 0;

        FORALL i IN 1 .. l_ids.COUNT
            UPDATE orders
            SET    status = 'PROCESSING',
                   updated_dt = SYSDATE
            WHERE  order_id = l_ids(i);

        COMMIT; -- 배치 단위 커밋으로 DML 잠금 슬롯 주기적 해제

    END LOOP;
    CLOSE c_orders;
EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK;
        CLOSE c_orders;
        RAISE;
END;
/
Enter fullscreen mode Exit fullscreen mode

원인 3 해결: 장시간 미완료 트랜잭션 탐지 및 정리

-- 장시간 활성 상태의 트랜잭션 탐지 (10분 이상)
SELECT s.sid,
       s.serial#,
       s.username,
       s.machine,
       s.program,
       s.status,
       s.last_call_et       AS idle_seconds,
       t.start_time,
       ROUND((SYSDATE - TO_DATE(t.start_time, 'MM/DD/YY HH24:MI:SS')) * 1440, 2) AS elapsed_min,
       t.used_ublk          AS undo_blocks_used
FROM   v$transaction t
JOIN   v$session s ON t.addr = s.taddr
WHERE  (SYSDATE - TO_DATE(t.start_time, 'MM/DD/YY HH24:MI:SS')) * 1440 > 10
ORDER BY elapsed_min DESC;
Enter fullscreen mode Exit fullscreen mode
-- 특정 세션이 보유한 DML Lock 목록 확인
SELECT l.sid,
       s.serial#,
       s.username,
       s.machine,
       o.owner,
       o.object_name,
       o.object_type,
       DECODE(l.lmode,
              0, 'None',
              1, 'Null',
              2, 'Row Share',
              3, 'Row Exclusive',
              4, 'Share',
              5, 'Share Row Exclusive',
              6, 'Exclusive') AS lock_mode
FROM   v$lock l
JOIN   v$session s ON l.sid = s.sid
JOIN   dba_objects o ON l.id1 = o.object_id
WHERE  l.type = 'TM'
  AND  l.sid = :target_sid; -- 조사할 SID 입력
Enter fullscreen mode Exit fullscreen mode
-- 문제 세션 강제 종료 (최후 수단)
-- 반드시 애플리케이션팀과 협의 후 수행할 것
ALTER SYSTEM KILL SESSION '&sid,&serial#' IMMEDIATE;

-- RAC 환경에서는 inst_id 추가
ALTER SYSTEM KILL SESSION '&sid,&serial#,@&inst_id' IMMEDIATE;
Enter fullscreen mode Exit fullscreen mode

예방 방법

1. DML_LOCKS 파라미터 모니터링 자동화 및 적정값 유지

DML 잠금 사용률이 임계치(예: 80%)를 초과하면 즉시 알림을 받을 수 있도록 모니터링을 구성합니다. 아래 쿼리를 스케줄러에 등록하거나 OEM(Oracle Enterprise Manager) Alert으로 설정해 두면 사전에 문제를 탐지할 수 있습니다.

-- DML_LOCKS 사용률 모니터링 쿼리 (주기적으로 실행)
SELECT p.value                              AS dml_locks_limit,
       COUNT(l.type)                        AS current_tm_locks,
       ROUND(COUNT(l.type) / p.value * 100, 2) AS usage_pct,
       CASE
           WHEN ROUND(COUNT(l.type) / p.value * 100, 2) >= 80
           THEN '⚠️  경고: DML Lock 사용률 80% 초과!'
           ELSE '✅ 정상'
       END AS status
FROM   v$parameter p
LEFT JOIN v$lock l ON l.type = 'TM'
WHERE  p.name = 'dml_locks'
GROUP BY p.value;
Enter fullscreen mode Exit fullscreen mode

운영 환경에서는 현재 최대 TM Lock 사용치의 최소 3~4배 여유를 두고 DML_LOCKS 값을 설정하는 것을 권장합니다. 분기마다 사용 추이를 검토하고, 업무량 증가에 따라 선제적으로 파라미터를 조정하는 관리 프로세스를 수립해야 합니다.

2. 애플리케이션 트랜잭션 설계 원칙 수립 및 코드 리뷰 프로세스 강화

트랜잭션은 가능한 한 짧게(Short Transaction) 유지하고, 한 트랜잭션 내에서 조작하는 테이블 수를 최소화하는 설계 원칙을 팀 내 개발 가이드라인으로 명문화합니다. 배치 프로그램 개발 시에는 반드시 적절한 배치 사이즈와 중간 커밋 로직을 코드 리뷰 체크리스트 항목으로 포함시켜, 문제가 되는 코드가 운영 환경에 배포되기 전에 사전 차단합니다. 또한 커넥션 풀 설정에서 connection-timeoutidle-timeout을 적절히 구성하여 좀비 트랜잭션이 누적되지 않도록 애플리케이션 레벨에서도 방어합니다.


관련 에러

에러 코드 설명
ORA-00060 deadlock detected while waiting for resource — DML 잠금 경합이 심화되면 데드락으로 이어질 수 있음
ORA-04031 unable to allocate shared memory — 잠금 관련 내부 구조체가 Shared Pool 메모리 부족으로 할당 실패할 때 발생
ORA-00054 resource busy and acquire with NOWAIT specified — 잠금 획득 대기 없이 즉시 실패를 요청했을 때 발생하는 관련 에러
ORA-02049 timeout: distributed transaction waiting for lock — 분산 트랜잭션 환경에서 DML 잠금 대기 시간 초과 시 발생
ORA-00257 archiver error — 아카이브 로그 공간 부족으로 DML 자체가 중단될 수 있어, 장시간 트랜잭션 유발 원인이 되기도 함

💡 DBA 팁: ORA-00055는 단독으로 발생하기보다 ORA-00060(데드락), ORA-00054(리소스 점유) 등과 함께 나타나는 경우가 많습니다. AWR(Automatic Workload Repository) 리포트에서 Enqueue Activity 섹션의 TM - Contention 항목을 주기적으로 점검하면 이 에러가 발생하기 전에 전조 증상을 포착할 수 있습니다.

Top comments (0)