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

ORA-01456
2026년 07월 16일 | DBMS Error 가이드

이 글에서 다루는 내용

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

ORA-01456 may not perform insert/delete/update inside a READ ONLY transaction 는?

ORA-01456 에러는 READ ONLY 트랜잭션으로 설정된 세션에서 INSERT, UPDATE, DELETE와 같은 데이터 변경(DML) 작업을 시도할 때 발생하는 에러입니다. Oracle은 SET TRANSACTION READ ONLY 명령을 통해 트랜잭션을 읽기 전용 모드로 설정할 수 있으며, 이 모드에서는 데이터 일관성을 보장하기 위해 어떠한 데이터 변경도 허용하지 않습니다. 주로 배치 리포트 작업이나 특정 시점의 일관된 데이터 조회가 필요한 상황에서 READ ONLY 트랜잭션을 사용하다가 실수로 DML을 수행할 때 이 에러가 발생합니다.


주요 발생 원인

1. 명시적인 SET TRANSACTION READ ONLY 설정 후 DML 수행

가장 흔한 원인으로, 개발자가 데이터 일관성을 위해 명시적으로 SET TRANSACTION READ ONLY를 선언한 후 해당 트랜잭션 내에서 DML 문을 실행하는 경우입니다. 특히 복잡한 리포트 프로시저 내부에서 집계 결과를 임시 테이블에 저장하려는 시도가 이 상황을 자주 유발합니다. 개발 단계에서 코드 리뷰 없이 배포될 경우 운영 환경에서 예상치 못한 에러로 이어질 수 있습니다.

2. 데이터베이스 링크(DB Link)를 통한 READ ONLY 트랜잭션 전파

분산 데이터베이스 환경에서 원격 데이터베이스로부터 READ ONLY 트랜잭션이 시작된 경우, 해당 세션에서 로컬 테이블에 DML을 수행하면 ORA-01456이 발생할 수 있습니다. 멀티 사이트 아키텍처에서 트랜잭션 전파 방식에 대한 이해 부족으로 발생하는 경우가 많습니다. 이는 디버깅이 상당히 복잡해질 수 있어 주의가 필요합니다.

3. 트리거 또는 저장 프로시저 내부에서의 잘못된 트랜잭션 제어

트리거나 패키지 프로시저 내부에서 SET TRANSACTION READ ONLY를 호출한 후 이후 로직에서 DML을 수행하는 경우에도 이 에러가 발생합니다. 복잡한 비즈니스 로직을 처리하는 패키지에서 트랜잭션 상태를 명확히 관리하지 않으면 예측하지 못한 지점에서 에러가 터질 수 있습니다. 특히 공통 유틸리티 프로시저가 READ ONLY를 설정하고 호출자가 이를 인지하지 못하는 경우 문제가 커집니다.


해결 방법

원인 1 해결: COMMIT 또는 ROLLBACK으로 트랜잭션 종료 후 DML 수행

READ ONLY 트랜잭션을 종료한 뒤 새로운 트랜잭션에서 DML을 수행해야 합니다.

-- 잘못된 코드 예시
SET TRANSACTION READ ONLY;

SELECT COUNT(*) FROM orders WHERE order_date = TRUNC(SYSDATE);

-- 아래에서 에러 발생!
INSERT INTO daily_summary (summary_date, order_count)
VALUES (TRUNC(SYSDATE), 100);
-- ORA-01456: may not perform insert/delete/update inside a READ ONLY transaction

-- 올바른 코드 예시
SET TRANSACTION READ ONLY;

-- 읽기 전용 작업 수행
SELECT COUNT(*) INTO v_count FROM orders WHERE order_date = TRUNC(SYSDATE);

-- READ ONLY 트랜잭션 종료
COMMIT;

-- 새로운 트랜잭션에서 DML 수행
INSERT INTO daily_summary (summary_date, order_count)
VALUES (TRUNC(SYSDATE), v_count);

COMMIT;

원인 2 해결: 트랜잭션 모드 확인 및 READ WRITE로 재설정

현재 세션의 트랜잭션 상태를 확인하고 필요에 따라 트랜잭션을 재설정합니다.

-- 현재 세션의 트랜잭션 정보 확인
SELECT s.sid,
       s.serial#,
       t.start_time,
       t.status,
       t.flags
FROM   v$session s,
       v$transaction t
WHERE  s.taddr = t.addr
AND    s.audsid = USERENV('SESSIONID');

-- READ ONLY 트랜잭션을 종료하고 READ WRITE 트랜잭션 시작
ROLLBACK;  -- 또는 COMMIT;

-- 명시적으로 READ WRITE 트랜잭션 설정
SET TRANSACTION READ WRITE;

-- 이제 DML 정상 수행 가능
UPDATE employees
SET    salary = salary * 1.1
WHERE  department_id = 10;

COMMIT;

원인 3 해결: 저장 프로시저 내 트랜잭션 로직 분리

프로시저를 읽기 전용 조회 부분과 DML 부분으로 명확히 분리합니다.

-- 잘못된 프로시저 예시
CREATE OR REPLACE PROCEDURE process_daily_report AS
    v_total_sales NUMBER;
BEGIN
    -- READ ONLY 트랜잭션 설정
    SET TRANSACTION READ ONLY;
    
    SELECT SUM(amount)
    INTO   v_total_sales
    FROM   sales
    WHERE  sale_date = TRUNC(SYSDATE);
    
    -- 여기서 ORA-01456 발생!
    INSERT INTO report_log (report_date, total_sales)
    VALUES (TRUNC(SYSDATE), v_total_sales);
    
    COMMIT;
END;
/

-- 올바른 프로시저 예시
CREATE OR REPLACE PROCEDURE process_daily_report AS
    v_total_sales NUMBER;
BEGIN
    -- 1단계: READ ONLY 트랜잭션으로 데이터 집계
    SET TRANSACTION READ ONLY;
    
    SELECT SUM(amount)
    INTO   v_total_sales
    FROM   sales
    WHERE  sale_date = TRUNC(SYSDATE);
    
    -- READ ONLY 트랜잭션 종료
    COMMIT;
    
    -- 2단계: 새로운 트랜잭션으로 결과 저장 (기본값 READ WRITE)
    INSERT INTO report_log (report_date, total_sales)
    VALUES (TRUNC(SYSDATE), v_total_sales);
    
    COMMIT;
    
EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK;
        -- 에러 로깅
        DBMS_OUTPUT.PUT_LINE('에러 발생: ' || SQLERRM);
        RAISE;
END;
/

현재 세션의 READ ONLY 트랜잭션 여부 확인 쿼리

-- 세션이 READ ONLY 트랜잭션인지 확인하는 방법
SELECT t.addr,
       t.xidusn,
       t.xidslot,
       t.xidsqn,
       DECODE(BITAND(t.flag, 1), 1, 'READ ONLY', 'READ WRITE') AS transaction_type,
       t.status
FROM   v$transaction t,
       v$session s
WHERE  t.addr = s.taddr
AND    s.audsid = USERENV('SESSIONID');

예방 방법

1. 트랜잭션 모드 관리 표준화 및 코드 리뷰 강화

팀 내에서 SET TRANSACTION READ ONLY를 사용하는 경우, 반드시 COMMIT 또는 ROLLBACK으로 해당 트랜잭션을 명시적으로 종료하고 이후 DML을 수행하도록 코딩 표준을 수립하세요. 코드 리뷰 체크리스트에 READ ONLY 트랜잭션 이후 DML 존재 여부를 반드시 포함시키고, 공통 패키지나 유틸리티에서 트랜잭션 상태를 변경할 경우 주석과 문서를 통해 호출자에게 명확히 알려야 합니다.

-- 권장되는 표준 패턴
-- Step 1: 읽기 전용 블록
SET TRANSACTION READ ONLY NAME 'Read-only reporting transaction';
-- ... 조회 로직 ...
COMMIT; -- 항상 명시적으로 종료

-- Step 2: 쓰기 블록 (필요 시)
-- SET TRANSACTION READ WRITE; -- 선택적 명시
-- ... DML 로직 ...
COMMIT;

2. 예외 처리 및 모니터링 체계 구축

애플리케이션 레벨에서 ORA-01456 에러를 명시적으로 처리하는 예외 핸들러를 구현하고, 해당 에러 발생 시 즉각적인 알림이 가도록 모니터링 체계를 구축하세요. Oracle의 Unified Auditing 또는 DBMS_APPLICATION_INFO를 활용해 트랜잭션 상태를 추적하면 문제 발생 시 빠른 원인 파악이 가능합니다.

-- 애플리케이션 예외 핸들러 예시
DECLARE
    e_read_only_tx EXCEPTION;
    PRAGMA EXCEPTION_INIT(e_read_only_tx, -1456);
BEGIN
    -- 비즈니스 로직
    SET TRANSACTION READ ONLY;
    -- ... 처리 로직 ...
    
EXCEPTION
    WHEN e_read_only_tx THEN
        ROLLBACK;
        -- 알림 발송 또는 에러 로깅
        INSERT INTO error_log (error_code, error_msg, occurred_at)
        VALUES (-1456, 'READ ONLY 트랜잭션 내 DML 시도', SYSDATE);
        COMMIT;
        RAISE_APPLICATION_ERROR(-20001, 'READ ONLY 트랜잭션 위반: DBA 팀에 문의하세요.');
END;
/

관련 에러

  • ORA-01453: SET TRANSACTION 명령은 트랜잭션의 첫 번째 문장이어야 한다는 에러로, ORA-01456과 함께 트랜잭션 제어 관련 에러입니다. DML 수행 후 SET TRANSACTION을 시도할 때 발생합니다.
  • ORA-01722: 트랜잭션 모드와 직접적인 관계는 없지만, 잘못된 DML 수행 시 함께 발생할 수 있는 숫자 변환 에러입니다.
  • ORA-02392: 세션 CPU 한도 초과 에러로, 장시간 실행되는 READ ONLY 트랜잭션과 관련하여 발생할 수 있습니다.
  • ORA-08177: can't serialize access for this transaction 에러로, SERIALIZABLE 격리 수준과 READ ONLY 트랜잭션을 함께 사용할 때 주의해야 할 연관 에러입니다.

DBMS 에러 코드 시리즈

주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.

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

댓글 남기기