2026년 06월 30일 | DBMS Error 가이드
이 글에서 다루는 내용
ORA-01037 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
ORA-01037 maximum cursor memory exceeded 는?
ORA-01037 에러는 Oracle 데이터베이스에서 커서(Cursor)가 사용할 수 있는 최대 메모리를 초과했을 때 발생하는 에러입니다. 커서는 SQL 문장을 실행하고 결과를 처리하기 위해 메모리를 할당받는데, 이 할당 가능한 메모리의 한계를 넘어서면 Oracle은 ORA-01037 에러를 반환합니다. 주로 매우 복잡한 쿼리, 과도한 커서 오픈, 또는 PGA 메모리 설정이 부족한 환경에서 빈번하게 발생하며, 운영 환경에서 갑작스럽게 나타나 서비스 장애로 이어질 수 있는 위험한 에러입니다.
주요 발생 원인
1. 과도한 커서 오픈 (Too Many Open Cursors)
애플리케이션에서 커서를 열고 닫지 않은 채로 계속 새로운 커서를 여는 경우, 세션당 커서 메모리 누적으로 인해 ORA-01037 에러가 발생합니다. 특히 루프(Loop) 안에서 동적 SQL을 반복 실행하거나, JDBC/OCI 연결에서 PreparedStatement를 명시적으로 닫지 않는 패턴이 대표적인 원인입니다. 이 경우 OPEN_CURSORS 파라미터를 늘리는 것만으로는 근본적인 해결이 어렵고, 반드시 커서 누수(Cursor Leak)를 수정해야 합니다.
2. PGA 메모리 부족 (Insufficient PGA Memory)
PGA(Program Global Area)는 각 서버 프로세스가 사용하는 개인 메모리 영역으로, 커서 실행에 필요한 정렬(Sort), 해시 조인(Hash Join), 바인드 변수 등의 정보를 저장합니다. PGA_AGGREGATE_TARGET 또는 PGA_AGGREGATE_LIMIT 값이 너무 낮게 설정된 경우, 복잡한 쿼리 실행 중 커서 메모리가 한계에 도달하여 ORA-01037이 발생합니다. 특히 대규모 배치 작업이나 복잡한 분석 쿼리가 동시에 다수 실행될 때 이 문제가 두드러집니다.
3. 매우 복잡한 SQL 구문 (Extremely Complex SQL Statements)
수십 개 이상의 조인(JOIN), 서브쿼리(Subquery), UNION, 윈도우 함수(Window Function)가 중첩된 매우 복잡한 SQL은 파싱(Parsing) 및 실행 계획 생성 과정에서 커서 메모리를 과도하게 소비합니다. Oracle의 쿼리 옵티마이저(Query Optimizer)는 복잡도에 비례하는 메모리를 커서에 할당하므로, 쿼리 자체의 구조를 단순화하지 않으면 에러가 반복됩니다. 이러한 경우 쿼리 리팩토링(Refactoring)이나 WITH 절(CTE)을 활용한 분리가 효과적입니다.
해결 방법
원인 1 해결: 커서 누수 진단 및 정리
현재 세션별 오픈된 커서 수를 확인하는 쿼리를 실행하여 커서 누수가 발생하는 세션을 식별합니다.
-- 세션별 오픈 커서 수 조회
SELECT s.sid,
s.serial#,
s.username,
s.program,
s.status,
COUNT(c.cursor_type) AS open_cursors
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.program, s.status
ORDER BY open_cursors DESC;
-- OPEN_CURSORS 파라미터 현재 값 확인
SHOW PARAMETER open_cursors;
-- OPEN_CURSORS 값 조정 (임시 조치 - 근본 원인 해결 후 원복 권장)
ALTER SYSTEM SET open_cursors = 1000 SCOPE=BOTH;
-- 커서 메모리 과다 사용 상세 정보 확인
SELECT sql_id,
sql_text,
users_opening,
users_executing
FROM v$sql
WHERE users_opening > 10
ORDER BY users_opening DESC;
원인 2 해결: PGA 메모리 설정 조정
PGA 관련 현재 설정과 실제 사용량을 확인한 후 적절한 값으로 조정합니다.
-- PGA 관련 파라미터 확인
SHOW PARAMETER pga;
-- PGA 실제 사용 현황 통계 조회
SELECT name, value
FROM v$pgastat
WHERE name IN (
'aggregate PGA target parameter',
'aggregate PGA auto target',
'global memory bound',
'total PGA inuse',
'total PGA allocated',
'maximum PGA allocated'
);
-- PGA_AGGREGATE_TARGET 값 조정 (실제 메모리 여유분 감안하여 설정)
ALTER SYSTEM SET pga_aggregate_target = 2G SCOPE=BOTH;
-- Oracle 12c 이상: PGA_AGGREGATE_LIMIT 조정 (PGA_AGGREGATE_TARGET의 2배 권장)
ALTER SYSTEM SET pga_aggregate_limit = 4G SCOPE=BOTH;
-- 세션별 PGA 사용량 상위 조회
SELECT s.sid,
s.serial#,
s.username,
p.pga_used_mem,
p.pga_alloc_mem,
p.pga_freeable_mem,
p.pga_max_mem
FROM v$session s
JOIN v$process p ON s.paddr = p.addr
WHERE s.username IS NOT NULL
ORDER BY p.pga_used_mem DESC
FETCH FIRST 20 ROWS ONLY;
원인 3 해결: 복잡한 SQL 리팩토링
복잡한 쿼리를 CTE(WITH 절)를 활용하여 단계별로 분리함으로써 커서 메모리 소비를 줄입니다.
-- 리팩토링 전: 중첩 서브쿼리가 과도한 복잡한 쿼리 (예시)
SELECT a.employee_id,
a.employee_name,
(SELECT SUM(b.salary)
FROM salaries b
WHERE b.dept_id IN (
SELECT c.dept_id
FROM departments c
WHERE c.region_id = (
SELECT d.region_id
FROM regions d
WHERE d.region_name = 'ASIA'
)
)
) AS total_salary
FROM employees a;
-- 리팩토링 후: WITH 절(CTE)로 단순화 (커서 메모리 부담 감소)
WITH asia_region AS (
SELECT region_id
FROM regions
WHERE region_name = 'ASIA'
),
asia_departments AS (
SELECT d.dept_id
FROM departments d
JOIN asia_region r ON d.region_id = r.region_id
),
dept_salary AS (
SELECT SUM(s.salary) AS total_salary
FROM salaries s
JOIN asia_departments ad ON s.dept_id = ad.dept_id
)
SELECT e.employee_id,
e.employee_name,
ds.total_salary
FROM employees e
CROSS JOIN dept_salary ds;
-- 쿼리 실행 계획으로 메모리 사용량 예측 확인
EXPLAIN PLAN FOR
SELECT * FROM employees WHERE department_id = 10;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY(format => 'ALL'));
예방 방법
1. 커서 사용 모니터링 자동화
정기적으로 커서 사용량을 모니터링하는 스크립트를 스케줄러(DBMS_SCHEDULER)에 등록하여, 임계값 초과 시 DBA에게 자동으로 알림이 발송되도록 구성합니다. AWR(Automatic Workload Repository) 리포트를 통해 커서 관련 통계를 주기적으로 분석하고, 개발 단계에서부터 커서 닫기(Close)를 코딩 표준으로 강제화하는 것이 중요합니다.
-- 커서 사용 임계값 모니터링 쿼리 (스케줄러 등록용)
SELECT sid,
serial#,
username,
COUNT(*) AS cursor_count
FROM v$open_cursor
WHERE cursor_type != 'OPEN'
GROUP BY sid, serial#, username
HAVING COUNT(*) > 500 -- 임계값 설정
ORDER BY cursor_count DESC;
2. 적절한 메모리 파라미터 사전 튜닝
운영 환경에 맞게 OPEN_CURSORS, SESSION_CACHED_CURSORS, PGA_AGGREGATE_TARGET 값을 사전에 튜닝하고 문서화합니다. 신규 서비스 오픈 전 반드시 부하 테스트(Load Test)를 수행하여 최대 부하 상황에서의 커서 및 PGA 메모리 사용량을 검증하고, 여유율 30% 이상을 확보한 상태에서 운영 환경에 반영합니다.
관련 에러
- ORA-01000: maximum open cursors exceeded — OPEN_CURSORS 파라미터로 설정된 최대 커서 수를 초과했을 때 발생하는 에러로, ORA-01037과 함께 가장 자주 마주치는 커서 관련 에러입니다.
- ORA-04031: unable to allocate bytes of shared memory — Shared Pool 메모리 부족 시 발생하며, PGA 메모리 부족(ORA-01037)과 함께 메모리 관련 에러로 동시에 나타나는 경우가 있습니다.
- ORA-01036: illegal variable name/number — 바인드 변수 처리 과정에서 커서 메모리와 연관되어 발생할 수 있는 에러입니다.
- ORA-00604: error occurred at recursive SQL level — 내부 재귀 SQL 수행 중 커서 메모리 초과로 ORA-01037과 함께 나타날 수 있습니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.