2026년 05월 21일 | Oracle DBA 가이드
📌 이 글에서 다루는 내용
ORA-01000 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
ORA-01000란?
ORA-01000 에러는 Oracle 데이터베이스에서 최대 오픈 커서 수를 초과했을 때 발생하는 에러입니다. Oracle은 세션당 동시에 열 수 있는 커서(Cursor)의 수를 OPEN_CURSORS 파라미터로 제한하며, 이 한계치를 넘어서면 더 이상 새로운 커서를 열 수 없게 됩니다. 주로 애플리케이션 코드에서 커서를 열고 닫지 않는 리소스 누수(Resource Leak)가 누적되어 발생하며, 운영 환경에서 갑작스럽게 서비스 장애로 이어질 수 있는 심각한 에러입니다.
주요 발생 원인
- 커서 미해제 (Cursor Leak)
애플리케이션에서 SQL을 실행한 후 커서를 명시적으로 닫지 않으면, 해당 커서는 세션이 종료될 때까지 계속 오픈 상태로 남아 있습니다. Java, Python 등의 애플리케이션에서 PreparedStatement나 ResultSet을 close() 하지 않는 경우가 대표적이며, 트래픽이 증가할수록 누적 속도가 빨라져 결국 한계에 도달합니다.
- 반복 루프 내 커서 재사용 미흡
PL/SQL 또는 애플리케이션 코드에서 루프 안에서 매번 새로운 커서를 열고 닫지 않으면 커서가 기하급수적으로 증가합니다. 특히 FOR 루프나 WHILE 루프 내부에서 동적 SQL(EXECUTE IMMEDIATE, OPEN cursor FOR)을 반복 사용할 때 커서 핸들을 제대로 관리하지 않으면 문제가 됩니다.
- OPEN_CURSORS 파라미터 값이 너무 낮게 설정됨
기본값 또는 초기 설정값이 실제 애플리케이션의 동시 커서 사용량에 비해 너무 작게 설정된 경우입니다. 개발 환경에서는 문제가 없다가 운영 환경에서 동시 접속자 수가 늘어나면서 갑자기 에러가 발생하는 패턴이 여기에 해당합니다.
- Connection Pool 설정 문제
커넥션 풀에서 커넥션을 재사용할 때, 이전 트랜잭션에서 닫히지 않은 커서가 남아 있는 상태로 커넥션이 반환되는 경우입니다. 풀링된 커넥션은 실제 DB 세션을 재사용하기 때문에, 커서 누수가 있는 커넥션이 반환되면 동일 세션에 커서가 계속 쌓이게 됩니다.
- PL/SQL 예외 처리 누락
PL/SQL 블록에서 예외가 발생했을 때 EXCEPTION 절에서 커서를 닫지 않으면 커서가 오픈 상태로 방치됩니다. 정상 경로에서는 커서를 닫더라도, 에러 경로에서 CLOSE cursor 구문이 없으면 예외 발생 시마다 커서가 누적됩니다.
해결 방법
1. 현재 오픈 커서 현황 확인
문제 발생 시 가장 먼저 현재 세션별 오픈 커서 현황을 파악해야 합니다.
-- 세션별 오픈 커서 수 확인
SELECT
s.sid,
s.serial#,
s.username,
s.machine,
s.program,
s.status,
COUNT(c.cursor_num) AS open_cursor_count
FROM
v$session s
JOIN
v$open_cursor c ON s.sid = c.sid
WHERE
s.username IS NOT NULL
GROUP BY
s.sid, s.serial#, s.username, s.machine, s.program, s.status
ORDER BY
open_cursor_count DESC;
-- 특정 세션에서 오픈된 커서 SQL 내용 확인
SELECT
c.sid,
c.cursor_num,
c.sql_text,
c.last_sql_active_time
FROM
v$open_cursor c
WHERE
c.sid = &target_sid -- 커서가 많은 SID 입력
ORDER BY
c.last_sql_active_time DESC;
2. OPEN_CURSORS 파라미터 확인 및 조정
-- 현재 OPEN_CURSORS 설정값 확인
SHOW PARAMETER OPEN_CURSORS;
-- 또는
SELECT name, value
FROM v$parameter
WHERE name = 'open_cursors';
-- 동적으로 값 변경 (재시작 불필요, 즉시 적용)
ALTER SYSTEM SET OPEN_CURSORS = 1000 SCOPE = BOTH;
-- 인스턴스 재시작 없이 현재 세션에만 적용
ALTER SESSION SET OPEN_CURSORS = 500;
> ⚠️ 주의: OPEN_CURSORS 값을 무작정 늘리는 것은 임시방편입니다. 근본 원인인 커서 누수를 반드시 함께 해결해야 합니다.
3. PL/SQL에서 커서 올바르게 관리하기
잘못된 예시 (커서 미해제):
-- 잘못된 방법: 예외 발생 시 커서가 닫히지 않음
DECLARE
CURSOR emp_cursor IS
SELECT employee_id, salary FROM employees WHERE department_id = 10;
v_emp_id employees.employee_id%TYPE;
v_salary employees.salary%TYPE;
BEGIN
OPEN emp_cursor;
LOOP
FETCH emp_cursor INTO v_emp_id, v_salary;
EXIT WHEN emp_cursor%NOTFOUND;
-- 처리 로직
DBMS_OUTPUT.PUT_LINE('ID: ' || v_emp_id || ', Salary: ' || v_salary);
END LOOP;
CLOSE emp_cursor;
-- 예외 발생 시 CLOSE가 실행되지 않음!
END;
/
올바른 예시 (예외 처리 포함):
-- 올바른 방법: EXCEPTION 절에서도 커서 닫기
DECLARE
CURSOR emp_cursor IS
SELECT employee_id, salary FROM employees WHERE department_id = 10;
v_emp_id employees.employee_id%TYPE;
v_salary employees.salary%TYPE;
BEGIN
OPEN emp_cursor;
LOOP
FETCH emp_cursor INTO v_emp_id, v_salary;
EXIT WHEN emp_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('ID: ' || v_emp_id || ', Salary: ' || v_salary);
END LOOP;
CLOSE emp_cursor;
EXCEPTION
WHEN OTHERS THEN
-- 커서가 열려 있으면 반드시 닫기
IF emp_cursor%ISOPEN THEN
CLOSE emp_cursor;
END IF;
RAISE; -- 예외 재발생
END;
/
FOR 루프 사용으로 커서 자동 관리 (권장):
-- FOR 루프는 커서를 자동으로 열고 닫아줌 (Best Practice)
BEGIN
FOR emp_rec IN (
SELECT employee_id, salary
FROM employees
WHERE department_id = 10
) LOOP
DBMS_OUTPUT.PUT_LINE(
'ID: ' || emp_rec.employee_id ||
', Salary: ' || emp_rec.salary
);
END LOOP;
-- 루프 종료 시 자동으로 커서 닫힘
END;
/
4. 동적 SQL에서 커서 관리
-- REF CURSOR를 사용한 동적 SQL 커서 관리
DECLARE
TYPE ref_cursor_type IS REF CURSOR;
v_cursor ref_cursor_type;
v_emp_id employees.employee_id%TYPE;
v_dept_id NUMBER := 10;
v_sql VARCHAR2(500);
BEGIN
v_sql := 'SELECT employee_id FROM employees WHERE department_id = :dept_id';
OPEN v_cursor FOR v_sql USING v_dept_id;
LOOP
FETCH v_cursor INTO v_emp_id;
EXIT WHEN v_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('Employee ID: ' || v_emp_id);
END LOOP;
CLOSE v_cursor; -- 반드시 명시적으로 닫기
EXCEPTION
WHEN OTHERS THEN
IF v_cursor%ISOPEN THEN
CLOSE v_cursor;
END IF;
RAISE;
END;
/
5. 문제 세션 강제 종료 (긴급 조치)
-- 커서 누수가 심한 세션 강제 종료 (운영 환경 신중하게 사용)
-- 먼저 대상 SID와 SERIAL# 확인
SELECT sid, serial#, username, machine, program
FROM v$session
WHERE sid = &target_sid;
-- 세션 강제 종료
ALTER SYSTEM KILL SESSION '&sid,&serial#' IMMEDIATE;
예방 방법
1. 애플리케이션 레벨에서 try-with-resources 패턴 사용
Java 기반 애플리케이션이라면 반드시 try-with-resources 구문을 사용하거나 finally 블록에서 Connection, PreparedStatement, ResultSet을 모두 닫아야 합니다. 코드 리뷰 체크리스트에 커서/커넥션 닫힘 여부
ORA 에러를 번호 순서대로 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
⚠ 본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.