2026년 06월 25일 | DBMS Error 가이드
이 글에서 다루는 내용
ORA-01000 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
ORA-01000 maximum open cursors exceeded 는?
ORA-01000 에러는 Oracle 데이터베이스에서 허용된 최대 오픈 커서 수를 초과했을 때 발생하는 에러입니다. Oracle은 세션당 동시에 열 수 있는 커서의 수를 OPEN_CURSORS 파라미터로 제한하며, 이 한도를 초과하면 새로운 커서를 열 수 없게 됩니다. 주로 애플리케이션에서 커서를 열고 닫지 않거나, 커넥션 풀 설정이 잘못된 경우, 또는 반복적인 쿼리 실행 시 커서 재사용이 이루어지지 않을 때 빈번하게 발생합니다.
주요 발생 원인
1. 애플리케이션에서 커서를 닫지 않는 경우 (커서 누수)
가장 흔한 원인으로, Java나 Python 등의 애플리케이션에서 PreparedStatement 또는 ResultSet 객체를 사용한 후 명시적으로 닫지 않으면 커서가 계속 열린 상태로 남습니다. 이는 커넥션이 오랜 시간 유지되는 서버 애플리케이션에서 특히 치명적이며, 시간이 지남에 따라 열린 커서 수가 누적되어 결국 ORA-01000을 유발합니다.
2. OPEN_CURSORS 파라미터 값이 너무 낮게 설정된 경우
데이터베이스 초기 설치 시 기본값(일반적으로 300)으로 설정된 OPEN_CURSORS 파라미터가 애플리케이션의 실제 커서 사용량에 비해 부족한 경우가 있습니다. 특히 대규모 트랜잭션 처리, 복잡한 PL/SQL 패키지 실행, 또는 다수의 동시 접속 사용자가 있을 때 이 기본값으로는 감당하기 어렵습니다.
3. 바인드 변수 미사용으로 인한 하드 파싱 증가
리터럴 값을 직접 SQL에 포함시켜 실행할 경우, Oracle은 매번 새로운 SQL로 인식하여 새 커서를 생성합니다. 같은 쿼리를 바인드 변수 없이 수천 번 실행하면 각각 별도의 커서가 생성되어 커서 풀이 급속도로 소진됩니다. 이는 성능 저하(하드 파싱)와 ORA-01000을 동시에 유발하는 이중 문제를 가져옵니다.
해결 방법
1. 현재 오픈 커서 상태 진단
먼저 현재 세션별 오픈 커서 현황을 파악합니다.
-- 세션별 오픈 커서 수 확인
SELECT s.sid,
s.serial#,
s.username,
s.machine,
s.program,
COUNT(c.cursor#) AS open_cursors
FROM v$session s
JOIN v$open_cursor c ON s.saddr = c.saddr
WHERE s.username IS NOT NULL
GROUP BY s.sid, s.serial#, s.username, s.machine, s.program
ORDER BY open_cursors DESC;
-- 현재 OPEN_CURSORS 파라미터 값 확인
SHOW PARAMETER open_cursors;
-- 또는
SELECT name, value
FROM v$parameter
WHERE name = 'open_cursors';
2. OPEN_CURSORS 파라미터 값 상향 조정
즉각적인 임시 해결책으로 OPEN_CURSORS 값을 증가시킵니다. 단, 이것은 근본 원인을 해결하지 않으므로 반드시 애플리케이션 점검과 병행해야 합니다.
-- 동적으로 변경 (재시작 불필요, 현재 세션에 즉시 적용)
ALTER SYSTEM SET open_cursors = 1000 SCOPE = BOTH;
-- 권장 값 확인을 위한 실제 사용량 모니터링
SELECT max_open_cur, cur_open_cur
FROM (
SELECT MAX(value) AS max_open_cur
FROM v$sesstat s
JOIN v$statname n ON s.statistic# = n.statistic#
WHERE n.name = 'opened cursors current'
),
(
SELECT SUM(value) AS cur_open_cur
FROM v$sesstat s
JOIN v$statname n ON s.statistic# = n.statistic#
WHERE n.name = 'opened cursors cumulative'
);
3. 커서 누수 유발 SQL 찾기
어떤 SQL이 반복적으로 커서를 열고 있는지 확인합니다.
-- 가장 많이 열린 커서의 SQL 텍스트 확인
SELECT c.sql_text,
c.cursor_type,
COUNT(*) AS cursor_count,
s.username,
s.machine
FROM v$open_cursor c
JOIN v$session s ON c.saddr = s.saddr
WHERE s.username IS NOT NULL
GROUP BY c.sql_text, c.cursor_type, s.username, s.machine
ORDER BY cursor_count DESC
FETCH FIRST 20 ROWS ONLY;
4. 바인드 변수 사용으로 커서 재사용
리터럴 값 대신 바인드 변수를 사용하여 커서 재사용률을 높입니다.
-- 잘못된 방법 (매번 새 커서 생성)
-- 애플리케이션에서 아래와 같이 실행하면 안 됨
SELECT * FROM employees WHERE employee_id = 101;
SELECT * FROM employees WHERE employee_id = 102;
SELECT * FROM employees WHERE employee_id = 103;
-- 올바른 방법 (바인드 변수 사용 - 커서 재사용)
SELECT * FROM employees WHERE employee_id = :emp_id;
-- PL/SQL에서 커서 명시적 닫기 예제
DECLARE
CURSOR emp_cur IS
SELECT employee_id, first_name, salary
FROM employees
WHERE department_id = :dept_id;
v_emp emp_cur%ROWTYPE;
BEGIN
OPEN emp_cur;
LOOP
FETCH emp_cur INTO v_emp;
EXIT WHEN emp_cur%NOTFOUND;
-- 처리 로직
DBMS_OUTPUT.PUT_LINE(v_emp.first_name || ': ' || v_emp.salary);
END LOOP;
CLOSE emp_cur; -- 반드시 명시적으로 닫아야 함!
EXCEPTION
WHEN OTHERS THEN
IF emp_cur%ISOPEN THEN
CLOSE emp_cur; -- 예외 발생 시에도 반드시 닫기
END IF;
RAISE;
END;
/
5. SESSION_CACHED_CURSORS 활용
세션 레벨에서 커서 캐싱을 활성화하여 파싱 오버헤드를 줄입니다.
-- 현재 SESSION_CACHED_CURSORS 값 확인
SHOW PARAMETER session_cached_cursors;
-- 값 변경 (세션 캐시 커서 수 증가)
ALTER SYSTEM SET session_cached_cursors = 100 SCOPE = SPFILE;
-- 세션 레벨에서도 변경 가능
ALTER SESSION SET session_cached_cursors = 100;
-- 커서 캐시 히트율 확인
SELECT name, value
FROM v$sysstat
WHERE name IN ('session cursor cache hits', 'parse count (total)')
ORDER BY name;
예방 방법
1. 정기적인 커서 사용 모니터링 자동화
운영 환경에서는 커서 사용량을 주기적으로 모니터링하는 스크립트나 OEM(Oracle Enterprise Manager) 알림을 설정해야 합니다. OPEN_CURSORS 값의 80% 이상 도달 시 경보를 발생시키고, AWR(Automatic Workload Repository) 리포트를 통해 커서 관련 통계를 주간 단위로 검토하는 루틴을 만들어 두는 것이 좋습니다.
-- 커서 사용률 임계치 모니터링 쿼리 (80% 이상 경고)
SELECT s.username,
s.sid,
s.machine,
COUNT(c.cursor#) AS open_cur,
p.value AS max_cur,
ROUND(COUNT(c.cursor#) / p.value * 100, 2) AS usage_pct,
CASE
WHEN ROUND(COUNT(c.cursor#) / p.value * 100, 2) >= 80
THEN '⚠️ 경고: 임계치 초과'
ELSE '정상'
END AS status
FROM v$session s
JOIN v$open_cursor c ON s.saddr = c.saddr
CROSS JOIN (
SELECT value FROM v$parameter WHERE name = 'open_cursors'
) p
WHERE s.username IS NOT NULL
GROUP BY s.username, s.sid, s.machine, p.value
HAVING COUNT(c.cursor#) >= p.value * 0.5
ORDER BY usage_pct DESC;
2. 애플리케이션 코드 레벨의 커서 관리 정책 수립
개발 표준에 반드시 커서/Statement/ResultSet의 명시적 close를 포함시키고, Java의 경우 try-with-resources 패턴을, Python의 경우 context manager(with 구문)를 의무적으로 사용하도록 코드 리뷰 체크리스트에 포함해야 합니다. 또한 커넥션 풀 설정 시 maxStatements 또는 Statement Cache 옵션을 적절히 구성하여 커서 재사용을 극대화하는 것이 중요합니다.
관련 에러
- ORA-01001: invalid cursor — 이미 닫혔거나 유효하지 않은 커서를 참조할 때 발생합니다. ORA-01000과 함께 커서 관리 부실에서 기인하는 경우가 많습니다.
- ORA-06511: PL/SQL: cursor already open — PL/SQL에서 이미 열린 커서를 다시 열려고 할 때 발생합니다. 커서 상태 확인(
%ISOPEN) 없이 OPEN을 반복 호출할 때 나타납니다. - ORA-04031: unable to allocate shared memory — Shared Pool 메모리 부족 시 발생하며, 하드 파싱 증가(바인드 변수 미사용)로 인해 ORA-01000과 연계되어 발생할 수 있습니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.