LLM WikiAccess-protected knowledge portal
← 스터디 홈
6편 · 약 11분

Glue·PySpark Full Load: 운영 DB를 보호하며 수십억 행 옮기기

대량 이관의 첫 목표는 속도가 아니라 영향 격리다

수십억 행을 운영 Aurora MySQL에서 직접 읽으면 버퍼 풀, 스토리지 I/O, network, long transaction이 실제 서비스와 경쟁한다. 빠른 이관을 위해 운영 트랜잭션을 느리게 만들면 목적을 잃는다.

리멤버 공개 사례는 Full Load 전용 Aurora replica를 만들고, AWS Glue PySpark가 JDBC로 복제본을 병렬 스캔해 S3 Tables에 쓰도록 구성했다. 운영 소스와 이관 워크로드를 먼저 분리한 뒤 병렬도를 올린 선택이다.

서비스Aurora writer
Full Load 전용 replica Glue PySpark JDBC partitions S3 Tables / Iceberg
전용 복제본을 이용한 Full Load

1. 전용 replica에서도 보아야 할 것

복제본은 운영 writer의 쿼리 자원과 읽기 부하를 분리한다. 그러나 무한한 복제본은 아니다.

  • replication lag가 커지면 Full Load 기준 시점이 흐려진다.
  • 지나친 JDBC 병렬 연결은 replica CPU·I/O를 포화시킨다.
  • 오래 열린 스냅샷은 InnoDB undo 정리에 영향을 줄 수 있다.
  • 네트워크·NAT·JDBC timeout이 실제 처리량의 경계가 될 수 있다.

따라서 replica를 만든 이후에도 CPU, Read IOPS, active connection, lag, long transaction을 같이 본다.

2. JDBC 병렬 읽기는 범위를 잘 나누는 문제다

Spark JDBC reader의 partitionColumn, lowerBound, upperBound, numPartitions는 동시 쿼리의 범위를 결정한다. 공개 사례는 테이블의 min/max ID를 구한 뒤 약 100만 건 단위로 처리하도록 파티션 수를 동적으로 조정했다.

이 방법의 함정은 아래와 같다.

  • ID 범위에 빈 구간이 많으면 파티션별 행 수가 균등하지 않다.
  • 특정 구간에 데이터가 몰리면 한 task만 늦게 끝나는 skew가 생긴다.
  • 파티션 수는 Spark 병렬도이자 DB 동시 connection 수다.
  • 작은 테이블을 너무 많은 task로 나누면 스케줄링과 연결 오버헤드가 더 커진다.

3. 타입 변환은 행 수 보다 먼저 검증한다

MySQL과 Spark·Parquet는 타입 세부 의미가 다르다. 공개 사례는 다음 경계를 따로 다뤄다.

  • ENUM·CHAR: padding이 추가되어 비교 결과가 달라지는지 확인
  • 1900년 이전 timestamp: Julian·Gregorian 재해석 차이 확인
  • decimal: precision·scale 손실 확인
  • zero date·invalid date: 실패, NULL, 보정 중 정책 결정
  • unsigned integer: 타깃 타입 범위 확인

행 수가 같아도 타임존이나 패딩이 바뀌면 데이터 의미는 달라진다. 그래서 타입별 표본, min/max, NULL 수, 고유값 수를 함께 비교한다.

4. ‘끝남’의 증거

이관 완료 조건은 작업 상태 SUCCESS가 아니다.

  1. 전체 테이블 목록과 누락 테이블 0
  2. 테이블별 행 수·PK 집합 대조
  3. 핵심 컬럼의 NULL·min/max·hash 대조
  4. 샤드·파티션별 표본 검증
  5. Full Load 기준점 이후 CDC lag 소진
  6. downstream 주요 쿼리의 결과·성능 비교
  7. 재실행·중단·rollback 절차와 근거 보존

References