python-oracledb executemany 메서드를 활용한 대량 데이터 처리

python-oracledb executemany 메서드는 파이썬을 이용한 오라클 대량 데이터 처리에 필수입니다. 데이터베이스 애플리케이션 개발 시 대량의 데이터를 효율적으로 삽입, 수정 또는 삭제하는 능력은 시스템 성능에 지대한 영향을 미칩니다. python-oracledb 라이브러리는 이러한 요구사항을 충족시키기 위해 Cursor.executemany() 메서드와 Cursor.setinputsizes() 메서드를 제공하여 데이터 처리의 효율성을 극대화합니다.

1. python-oracledb executemany : 배치 실행을 통한 네트워크 오버헤드 최소화

관계형 데이터베이스 시스템과의 통신은 네트워크 왕복(round-trip) 비용이 발생합니다. SQL 문을 실행할 때마다 이 네트워크 비용이 항상 발생합니다.

대량 데이터를 처리하는 전통적인 방식은 Cursor.execute() 메서드를 반복하여 호출하는 것입니다. N건의 대량의 데이터를 처리할 때 N번의 네트워크 왕복이 발생하여 성능 저하의 주된 원인이 됩니다.

Cursor.executemany() 메서드는 하나의 SQL 문을 실행할 때 처리할 데이터를 한 번에 데이터베이스로 전송함으로써 단 한 번의 네트워크 왕복으로 다수의 작업을 완료하도록 설계되었습니다. 이는 네트워크 지연 시간과 데이터베이스 서버의 부하를 줄이고 전반적인 트랜잭션 처리 능력을 높여줍니다.

python-oracledb executemany 활용 예시

10개의 컬럼을 가진 LargeTable에 데이터를 삽입하는 예시를 통해 executemany()의 효율성을 살펴보겠습니다.

예시의 LargeTable은 다음과 같이 정의합니다.

CREATE TABLE LargeTable (
    Col1 NUMBER,          
    Col2 VARCHAR2(50),    
    Col3 VARCHAR2(100),   
    Col4 VARCHAR2(200),   
    Col5 DATE
);

여러 건의 레코드를 삽입하는 파이썬 코드는 다음과 같습니다.

import oracledb

# 데이터베이스 연결 및 커서 생성 (예시)
connection = oracledb.connect(user="user", password="password", dsn="dsn")
cursor = connection.cursor()

data = [ (1, "Text A", "Longer text example A","Very long content A goes here", oracledb.Date(2025, 6, 2) ),
         (2, "Text B", "Longer text example B","Very long content B goes here", oracledb.Date(2025, 6, 3) ),
         (3, "Text C", "Longer text example C","Very long content C goes here", oracledb.Date(2025, 6, 4) )
] 

# 단 한 번의 호출로 여러 레코드 삽입
cursor.executemany("""
    INSERT INTO LargeTable (Col1, Col2, Col3, Col4, Col5)
    VALUES (:1, :2, :3, :4, :5)
""", data)

connection.commit()
print(f"{cursor.rowcount} 개의 행이 삽입되었습니다.")

이 방식은 INSERT 문뿐만 아니라 UPDATE, DELETE, MERGE와 같은 모든 DML(Data Manipulation Language) 문에 적용 가능하며, 대량의 데이터 조작 시 성능 최적화를 위한 필수적인 기법입니다.

2. setinputsizes(): 메모리 버퍼 사전 할당을 통한 오버헤드 제거

python-oracledb executemany 는 네트워크 왕복 횟수를 줄여주지만, python-oracledb가 데이터를 처리하는 방식 자체에서 발생하는 내부적인 오버헤드가 존재할 수 있습니다. 특히 가변 길이 데이터 타입(예: 문자열)을 처리할 때 이러한 현상이 두드러집니다.

python-oracledb는 기본적으로 각 컬럼의 데이터 타입을 추정하고, 문자열/바이트 데이터의 경우 가장 긴 값이 나타날 때마다 동적으로 버퍼 크기를 조정합니다. 이러한 동적 메모리 재할당 및 데이터 복사 작업이 반복될수록 시스템 자원을 소모하고 성능 저하를 야기합니다.

Cursor.setinputsizes() 메서드는 이러한 오버헤드를 제거하기 위해 사용됩니다. 이 메서드를 통해 python-oracledb에게 각 바인드 변수의 데이터 타입과 예상되는 최대 크기를 미리 알려줄 수 있습니다. 이는 SQL 실행 전에 충분한 크기의 메모리 버퍼를 할당하도록 하여 불필요한 재할당과 데이터 복사를 방지합니다.

setinputsizes()의 활용 예시

위의 LargeTable 삽입 예시를 통해 setinputsizes()의 효과를 확인해 보겠습니다.

import oracledb

connection = oracledb.connect(user="user", password="password", dsn="dsn")
cursor = connection.cursor()

data = [ (1, "Text A", "Longer text example A","Very long content A goes here", oracledb.Date(2025, 6, 2) ),
         (2, "Text B", "Longer text example B","Very long content B goes here", oracledb.Date(2025, 6, 3) ),
         (3, "Text C", "Longer text example C","Very long content C goes here", oracledb.Date(2025, 6, 4) )
] 

# 5개 컬럼에 대한 setinputsizes 설정
# 각 인자는 SQL 문 바인드 변수 순서와 일치하며, 타입과 예상 최대 길이를 명시
cursor.setinputsizes(
    None,      # Col1: NUMBER
    50,        # Col2: VARCHAR2(50) - 최대 50자
    100,       # Col3: VARCHAR2(100) - 최대 200자
    200,       # Col4: VARCHAR2(200) - 최대 200자
    None       # Col5: DATE
)

cursor.executemany("""
    INSERT INTO LargeTable (Col1, Col2, Col3, Col4, Col5)
    VALUES (:1, :2, :3, :4, :5)
""", data)

connection.commit()
print(f"{cursor.rowcount} 개의 행이 삽입되었습니다.")

여기서 cursor.setinputsizes()는 5개의 컬럼에 대해 각각의 특성에 맞춰 설정됩니다.

  • None: NUMBER, DATE, TIMESTAMP, CLOB, BLOB과 같이 python-oracledb가 이미 효율적으로 처리하거나 내부적으로 특수하게 관리하는 데이터 타입에 사용됩니다.
  • 정수 값: VARCHAR2와 같은 문자열 컬럼의 경우, 해당 컬럼에 저장될 최대 문자(character) 수를 정수로 지정합니다. 예를 들어, VARCHAR2(50) 컬럼에는 50을, VARCHAR2(200) 컬럼에는 200을 명시합니다. 한글과 같은 멀티바이트 문자도 문자 수 기준으로 지정하며, python-oracledb가 내부적으로 바이트 수로 변환하여 적절한 버퍼를 할당합니다.

이러한 사전 할당을 통해, 데이터가 길어질 때마다 발생하는 동적인 버퍼 크기 조정 및 데이터 복사 오버헤드를 완전히 제거하여 성능을 최적화할 수 있습니다.

setinputsizes() 사용 시 고려사항

  • 정확한 길이 지정: 문자열 컬럼의 경우, 실제 들어올 데이터의 최대 문자 수를 지정하는 것이 중요합니다.
  • 과도한 크기 할당 지양: 필요한 크기보다 훨씬 큰 값을 지정하면 불필요한 메모리 낭비가 발생할 수 있습니다.
  • 2GB 버퍼 제한: 할당되는 버퍼의 총 크기가 2GB를 초과할 경우 DPI-1015: array size of <n> is too large 오류가 발생할 수 있습니다. 이 경우, executemany() 호출 시 한 번에 처리하는 레코드의 수를 조정해야 합니다.

3. 정리

python-oracledbCursor.executemany()Cursor.setinputsizes() 메서드는 대량 데이터를 처리하는 애플리케이션의 성능을 극대화하는 데 필수적인 도구입니다. executemany()를 통한 네트워크 왕복 최소화와 setinputsizes()를 통한 효율적인 메모리 버퍼 관리는 개발자가 고성능의 데이터베이스 연동 시스템을 구축하는 데 핵심적인 역할을 수행합니다. 이러한 최적화 기법을 적극적으로 활용하여 안정적이고 효율적인 데이터 처리 솔루션을 구현하시길 바랍니다.

참고 자료: python-oracledb 공식 문서 – Executing Batch Statements and Bulk Loading (이 글의 주요 참고 자료입니다.)

위로 스크롤