Backend/Data

MySQL에서 Oracle로, 대용량 데이터를 안전하게 이관한 방법 (Python 사용기)

gigyesik 2025. 5. 16. 18:40
반응형

썸네일 이미지

MySQL에서 Oracle로, 대용량 데이터를 안전하게 이관한 방법 (Python 사용기)

대용량 데이터를 MySQL에서 Oracle로 옮겨야 하는 일이 생겼다.
데이터 양은 약 300만 건. 단순히 insert 문을 반복하기엔 너무 방대했고, 실수하면 복구도 어렵다.

이 글은 그때 내가 Python으로 병렬 이관 스크립트를 작성해, 빠르고 안정적으로 이관을 마친 과정을 공유하는 글이다.


이런 상황이었어요

  • 출발지: MySQL
  • 도착지: Oracle
  • 데이터량: 300만 건 이상
  • 문제: 중복 데이터 발생 가능성 + 너무 오래 걸림 + 재처리 불가

어떤 전략으로 해결했을까요?

✔ 1. ID 범위로 데이터를 나눠 병렬 처리

단일 쓰레드로 300만 건을 처리하는 건 너무 느렸다.
그래서 전체 데이터를 ID 기준으로 세 구간으로 나누고, 세 개의 쓰레드로 병렬 실행했다.

쓰레드 처리 범위 (예시)
Thread 1 ID 1 ~ 1,000,000
Thread 2 ID 1,000,001 ~ 2,000,000
Thread 3 ID 2,000,001 ~ 3,000,000

이렇게 나누면 특정 구간만 다시 실행하는 것도 가능해서 유지보수도 훨씬 편해진다.


✔ 2. 중복 insert는 '예외로 무시'하는 방식 사용

Oracle은 같은 데이터를 중복으로 넣으려고 하면 ORA-00001 오류가 난다.
처음에는 MERGE 문도 고민했지만, 성능이 좋지 않아서 결국 단순 insert + 중복 예외 무시 전략을 썼다.

try:
    cursor.execute("INSERT INTO ... VALUES ...")
except cx_Oracle.IntegrityError as e:
    if 'ORA-00001' in str(e):
        pass  # 이미 있으면 무시
    else:
        raise

간단하지만, 속도도 빠르고 안정성도 확보할 수 있는 현실적인 방법이었다.


✔ 3. Python으로 만든 이관 스크립트

대략적인 코드 구조는 다음과 같다:

from concurrent.futures import ThreadPoolExecutor

def migrate_range(start_id, end_id):
    # MySQL에서 조회 → Oracle에 insert
    # insert 시 ORA-00001 예외는 무시 처리
    ...

ranges = [
    (1, 1000000),
    (1000001, 2000000),
    (2000001, 3000000)
]

with ThreadPoolExecutor(max_workers=3) as executor:
    for start_id, end_id in ranges:
        executor.submit(migrate_range, start_id, end_id)
  • MySQL에는 pymysql 사용
  • Oracle에는 cx_Oracle 사용
  • 날짜 포맷은 SQL 쿼리 안에서 TO_TIMESTAMP()로 처리

결과는 어땠을까요?

  • ⏱ 기존 2분 30초 → 55초로 단축
  • ⚠️ 중복 데이터 무시 성공률 100%
  • 🔁 부분 실패 시 구간만 재실행 가능

덕분에 운영 중인 데이터를 안정적으로 마이그레이션할 수 있었다.


마무리하며

이번 작업을 하면서 느낀 점은 이거였다:

데이터 이관은 정확도보다도 “복구 가능성”을 설계하는 게 더 중요하다.

혹시 비슷한 이관 작업을 준비 중이라면,
병렬 처리 + 예외 무시 + 구간 분할 전략을 꼭 한 번 고려해보시길 바란다.

더욱 자세한 글은 velog MySQL → Oracle 대용량 데이터 이관 – 병렬 처리와 예외 처리 전략 (Python 기반) 에서 확인할 수 있다.

반응형