TL;DR

  • MySQL과 Oracle은 서버 커서 구현 방식이 근본적으로 다름
  • MySQL: 임시 테이블 생성하여 전체 결과 저장
  • Oracle: PGA 메모리에서 커서 상태만 유지하고 필요한 만큼 페치
  • “클라이언트 사이드 커서"는 MySQL에서 공식 용어가 아님
  • MySQL: 클라이언트 버퍼링, 스트리밍, 서버 커서 3가지 방식 제공

1. 커서란 무엇인가

데이터베이스 커서는 쿼리 결과를 순차적으로 처리하기 위한 메커니즘입니다. 대용량 결과를 한 번에 메모리로 로드하지 않고, 필요한 만큼만 가져와서 처리할 수 있게 해줍니다.

데이터베이스에서는 결과를 처리하는 위치에 따라 서버 커서클라이언트 커서으로 구분할 수 있습니다.

커서의 필요성

-- 100만 건의 데이터를 한 번에 처리?
SELECT * FROM large_table; -- 메모리 부족!

-- 커서를 사용하면?
-- 필요한 만큼만 순차적으로 처리 가능

2. 하지만 혼란스러운 “클라이언트 커서”

일반적인 데이터베이스 용어

SQL Server, PostgreSQL 등 많은 데이터베이스에서는 “클라이언트 사이드 커서(Client-side Cursor)“라는 용어를 공식적으로 사용합니다. 이는 전체 결과를 클라이언트 메모리로 가져와서 클라이언트 측에서 커서를 관리하는 방식을 의미합니다.

MySQL에서는 부적절한 이유

MySQL 공식 문서는 “클라이언트 사이드 커서"라는 용어를 사용하지 않습니다. 대신 MySQL은 결과 처리 방식을 다음과 같이 명확히 구분합니다:

  • 클라이언트 버퍼링 (mysql_store_result())
    • 전체 결과를 클라이언트 메모리로 한 번에 로드
    • 장점: 빠른 순회, 역방향 이동 가능
    • 단점: 대용량 데이터 시 메모리 부족
  • 스트리밍 (mysql_use_result())
    • 서버에서 결과를 연속으로 전송, 클라이언트가 순차 처리
    • 장점: 메모리 효율적
    • 단점: 한 방향만 가능, 연결 유지 필요
  • 서버 커서 (Server-side Cursor)
    • 서버에 커서 상태 유지, 필요한 만큼만 전송
    • 장점: 메모리 효율적, 유연한 페치
    • 단점: 서버 리소스 사용, 설정 필요

MySQL 문맥에서 “클라이언트 커서"라고 표현하면 혼란을 줄 수 있습니다. 실제로는 단순히 전체 결과를 클라이언트 메모리에 로드하는 것일 뿐, 진짜 “커서"가 아니기 때문입니다.

올바른 용어 사용

환경 적절한 표현
MySQL 전문가와 대화 “클라이언트 버퍼링”, “전체 결과 로드”
일반 DB 개발자와 대화 “클라이언트 사이드 커서” (통용됨)
기술 문서 작성 “클라이언트 측 결과 버퍼링”
  • 권장: MySQL 관련 글에서는 “클라이언트 버퍼링"이 더 정확합니다.

3. MySQL 서버 커서

3.1 SQL 콘솔에서

MySQL 콘솔에서는 Stored Procedure 내부에서만 커서를 사용할 수 있습니다.

3.2 클라이언트 라이브러리에서 (JDBC)

JDBC를 통해서는 일반 SELECT 쿼리에도 서버 커서를 사용할 수 있습니다.

String url = "jdbc:mysql://localhost:3306/db?useCursorFetch=true";
PreparedStatement pstmt = conn.prepareStatement(
    "SELECT * FROM employees",
    ResultSet.TYPE_FORWARD_ONLY,
    ResultSet.CONCUR_READ_ONLY
);
pstmt.setFetchSize(100);
ResultSet rs = pstmt.executeQuery();

필수 조건:

  1. useCursorFetch=true (JDBC URL 파라미터)
  2. setFetchSize(n > 0) 설정

3.3 내부 동작: 임시 테이블 방식

MySQL의 서버 커서는 내부 임시 테이블로 구현됩니다.

쿼리 실행
    ↓
전체 결과를 임시 테이블에 저장
- MEMORY 테이블 (작은 경우)
- MyISAM 테이블 (큰 경우, max_heap_table_size 초과 시)
    ↓
클라이언트 요청 시 fetchSize만큼 전송

특징:

  • 전체 결과가 임시 테이블에 구체화됨
  • 초기 구체화 시간이 필요함
  • 대용량 결과 시 디스크 사용 가능
  • 메모리 효율성이 상대적으로 낮음

4. Oracle 서버 커서

4.1 SQL 콘솔에서

Oracle은 SQL 콘솔에서 직접 커서를 사용할 수 있습니다. 이때 모든 SQL 문이 자동으로 암시적 커서를 사용합니다.

4.2 클라이언트 라이브러리에서 (JDBC)

Oracle JDBC는 setFetchSize()만으로 서버 커서가 활성화됩니다.

String url = "jdbc:oracle:thin:@localhost:1521:orcl";
PreparedStatement pstmt = conn.prepareStatement(
    "SELECT * FROM employees"
);
pstmt.setFetchSize(100);
ResultSet rs = pstmt.executeQuery();

필수 조건:

  1. setFetchSize(n > 0) (이것만 필요)

4.3 내부 동작: PGA 메모리 방식

Oracle은 PGA(Program Global Area) 메모리에서 커서 상태를 유지합니다.

쿼리 실행
    ↓
결과 집합 포인터를 PGA에 생성
(실제 데이터는 Buffer Cache에 유지)
    ↓
클라이언트 요청 시 필요한 행만 전송

특징:

  • 임시 테이블을 생성하지 않음
  • 전체 결과를 미리 구체화하지 않음
  • 세션별 PGA 사용으로 격리됨
  • 메모리 효율적
  • On-demand 페치로 성능 우수

5. MySQL vs Oracle 비교

사용 방법

SQL 콘솔

  • MySQL: Stored Procedure 내부에서만 가능
  • Oracle: PL/SQL 블록에서 직접 사용 가능, 암시적 커서 자동 적용

JDBC 설정

  • MySQL: useCursorFetch=true + setFetchSize(n > 0) 필수
  • Oracle: setFetchSize(n > 0) 만 필요

내부 동작 방식

동작 방식

  • MySQL: 쿼리 실행 시 전체 결과를 임시 테이블에 저장 (MEMORY → MyISAM → 디스크, 초기 오버헤드 높음)
  • Oracle: 세션별 PGA에 커서 상태만 유지, 필요한 만큼 On-demand 페치 (즉시 시작)

성능 특성

MySQL 서버 커서

  • 전체 결과를 미리 저장하므로 초기 대기 시간 발생
  • 메모리/디스크 사용량이 높지만 결과 일관성 보장
  • 작은 결과 집합이나 여러 번 순회가 필요한 경우 적합

Oracle 서버 커서

  • 필요한 만큼만 페치하므로 즉시 시작 가능
  • 메모리 효율적이지만 세션 상태 관리 필요
  • 대용량 결과 집합이나 스트리밍 처리에 적합

6. 다른 데이터베이스는?

PostgreSQL

Oracle과 유사한 방식으로 서버 커서를 지원합니다.

  • SQL 콘솔에서 DECLARE CURSOR 사용 가능
  • FETCH 방향 제어 가능 (FORWARD, BACKWARD)
  • 트랜잭션 내에서만 유효

SQL Server

클라이언트/서버 사이드 커서를 모두 명시적으로 구분하여 지원합니다.

  • ADO에서 CursorLocation 속성으로 선택
  • adUseClient: 클라이언트 사이드 커서
  • adUseServer: 서버 사이드 커서
  • 성능상 커서보다 SET 기반 처리 권장

참고 문서

MySQL

Oracle