728x90

안녕하세요, 오늘은 Python에서 두 오라클 서버 간 데이터를 이동할 때, 20만 건 정도에서 타임아웃으로 연결이 끊어지는 문제를 해결하는 방법을 다뤄보겠습니다. 순수 Python 코드로만 문제를 풀어보며, 실무에서 바로 적용할 수 있는 최적화 기법을 소개합니다.
문제 상황
두 개의 오라클 서버를 Python(cx_Oracle)로 연결해 테이블 데이터를 이동시키던 중, 약 20만 건에서 타임아웃이 발생하며 연결이 끊어졌습니다. 이 문제는 네트워크, 데이터베이스 설정, 또는 코드 비효율성에서 비롯될 가능성이 높습니다. 아래에서 원인을 분석하고 해결책을 제시합니다.
원인 분석
-
오라클 세션 타임아웃: 서버 설정이나 cx_Oracle 연결의 기본 타임아웃이 짧음.
-
대량 데이터 부하: 한 번에 20만 건을 처리하며 메모리와 네트워크에 과부하.
-
트랜잭션 크기: 대량 삽입 시 트랜잭션이 커져 타임아웃 발생.
-
커서 리소스 부족: 단일 커서로 대량 데이터를 처리하며 리소스가 고갈됨.
해결 방법
이 문제를 해결하려면 다음 전략을 적용합니다:
-
배치 처리: 데이터를 1만 건씩 나눠 처리해 부하 감소.
-
타임아웃 설정 조정: 연결 유지 시간을 늘리거나 확인 로직 추가.
-
효율적 삽입: executemany로 여러 행을 한 번에 처리.
-
에러 처리: 타임아웃 시 재연결로 안정성 확보.
728x90
728x90
아래는 최적화된 샘플 코드입니다.
최적화된 Python 코드
import cx_Oracle
from datetime import datetime
# 배치 크기 설정
BATCH_SIZE = 10000
# 소스 서버 연결
dsn1 = cx_Oracle.makedsn("source_host", 1521, service_name="source_service")
conn1 = cx_Oracle.connect(user="source_user", password="source_password", dsn=dsn1)
# 타겟 서버 연결
dsn2 = cx_Oracle.makedsn("target_host", 1521, service_name="target_service")
conn2 = cx_Oracle.connect(user="target_user", password="target_password", dsn=dsn2)
try:
# 커서 생성
cursor1 = conn1.cursor() # 소스 DB
cursor2 = conn2.cursor() # 타겟 DB
# 소스 데이터 조회
cursor1.execute("SELECT id, name, created_date FROM source_table")
# 타겟 테이블 준비
cursor2.execute("""
CREATE TABLE IF NOT EXISTS target_table (
id NUMBER,
name VARCHAR2(100),
created_date DATE
)
""")
# 배치 단위 처리
rows = cursor1.fetchmany(BATCH_SIZE)
total_processed = 0
while rows:
start_time = datetime.now()
# 효율적 삽입
cursor2.executemany("""
INSERT INTO target_table (id, name, created_date)
VALUES (:1, :2, :3)
""", rows)
conn2.commit()
total_processed += len(rows)
print(f"{total_processed}건 처리 완료 - 소요 시간: {datetime.now() - start_time}")
# 다음 배치
rows = cursor1.fetchmany(BATCH_SIZE)
print("데이터 이동 완료!")
except cx_Oracle.DatabaseError as e:
print(f"오류 발생: {e}")
conn2.rollback()
finally:
# 리소스 정리
cursor1.close()
cursor2.close()
conn1.close()
conn2.close()
코드 개선 포인트
-
배치 크기 조정
-
BATCH_SIZE를 10,000으로 설정했지만, 서버 성능에 따라 5,000~50,000으로 조정 가능합니다.
-
-
타임아웃 늘리기
-
소켓 타임아웃을 늘려 연결 끊김 방지:
import socket socket.setdefaulttimeout(600) # 10분
-
-
연결 유지 확인
-
주기적으로 연결 상태 점검:
if total_processed % 50000 == 0: conn1.ping() conn2.ping() print("연결 상태 확인")
-
-
에러 처리 강화
-
타임아웃 시 재연결:
except cx_Oracle.OperationalError as e: print(f"연결 오류: {e}, 재연결 시도") conn1 = cx_Oracle.connect(user="source_user", password="source_password", dsn=dsn1) conn2 = cx_Oracle.connect(user="target_user", password="target_password", dsn=dsn2) cursor1 = conn1.cursor() cursor2 = conn2.cursor() continue
-
-
성능 최적화
-
소스와 타겟 테이블에 인덱스를 추가해 속도 개선:
CREATE INDEX idx_source_id ON source_table(id);
-
기대 효과
-
타임아웃 해결: 배치 처리로 부하를 줄여 20만 건 이상 처리 가능.
-
속도 향상: executemany로 네트워크 호출 최소화.
-
안정성: 에러 처리와 재연결로 중단 없이 실행.
추가 팁
-
대용량 데이터: 행 크기가 크다면(예: CLOB), BATCH_SIZE를 더 줄임.
-
네트워크 점검: 서버 간 대역폭이 낮으면 DBA와 협의.
-
로그 추가: 진행 상황을 파일로 남기려면 logging 모듈 활용.
마무리
이 코드를 적용하면 타임아웃 없이 두 오라클 서버 간 데이터 이동이 가능할 겁니다. 만약 문제가 지속되거나 에러가 발생하면, 구체적인 메시지를 공유해 주시면 더 세밀히 도와드리겠습니다. 데이터 이동 작업이 성공적으로 마무리되길 바랍니다!
728x90
'Python을 배워보자' 카테고리의 다른 글
Flask로 RESTful API 구현 및 Vue.js와의 CORS 문제 해결 (0) | 2025.03.01 |
---|---|
Git Push 알림 시스템: Python으로 Windows 트레이 아이콘과 팝업 구현(터미널 없이 Git Push 알림 실행)하기 (0) | 2025.02.27 |
파이썬에서 복잡한 딕셔너리 구조 탐색하기: 다양한 방법과 예제 (0) | 2025.02.21 |
Python으로 특정 경로에서 파일 찾기: 경로 리스트 출력하기 (0) | 2025.02.20 |
Jupyter Notebook을 RESTful API 서버로 변신시키는 notebook_http 확장: 데이터 분석 파이프라인 자동화의 길을 열다 (0) | 2024.12.27 |