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';
-- 현재 시스템에서 사용 중인 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;
-- 현재 활성화된 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;
충분한 여유값을 확인한 후 파라미터를 동적으로 변경합니다. 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';
⚠️ 주의:
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;
/
-- 대량 배치 처리 시 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;
/
원인 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;
-- 특정 세션이 보유한 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 입력
-- 문제 세션 강제 종료 (최후 수단)
-- 반드시 애플리케이션팀과 협의 후 수행할 것
ALTER SYSTEM KILL SESSION '&sid,&serial#' IMMEDIATE;
-- RAC 환경에서는 inst_id 추가
ALTER SYSTEM KILL SESSION '&sid,&serial#,@&inst_id' IMMEDIATE;
예방 방법
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;
운영 환경에서는 현재 최대 TM Lock 사용치의 최소 3~4배 여유를 두고 DML_LOCKS 값을 설정하는 것을 권장합니다. 분기마다 사용 추이를 검토하고, 업무량 증가에 따라 선제적으로 파라미터를 조정하는 관리 프로세스를 수립해야 합니다.
2. 애플리케이션 트랜잭션 설계 원칙 수립 및 코드 리뷰 프로세스 강화
트랜잭션은 가능한 한 짧게(Short Transaction) 유지하고, 한 트랜잭션 내에서 조작하는 테이블 수를 최소화하는 설계 원칙을 팀 내 개발 가이드라인으로 명문화합니다. 배치 프로그램 개발 시에는 반드시 적절한 배치 사이즈와 중간 커밋 로직을 코드 리뷰 체크리스트 항목으로 포함시켜, 문제가 되는 코드가 운영 환경에 배포되기 전에 사전 차단합니다. 또한 커넥션 풀 설정에서 connection-timeout 및 idle-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)