데이터베이스 마이그레이션 실무 가이드: 무중단 전환을 위한 전략과 구현

데이터베이스 마이그레이션의 필요성

데이터베이스 마이그레이션은 단순히 데이터를 옮기는 작업을 넘어, 서비스의 안정성과 확장성을 확보하기 위한 핵심적인 과정입니다. 흔히 이사에 비유되곤 하는 이 작업은 다음과 같은 기술적 요구 사항으로 인해 발생합니다.

  • 인프라 확장성 확보: 데이터 유입량이 급증하여 현재 스토리지나 컴퓨팅 자원으로는 감당하기 어려운 경우.
  • 기술 부채 해소: 보안 취약점이 발견된 구형 엔진(예: MySQL 5.6 이하)을 최신 버전으로 업그레이드해야 하는 경우.
  • 성능 최적화: 단일 인스턴스의 부하를 분산하기 위해 샤딩을 도입하거나 클라우드 네이티브 환경으로 이전하는 경우.
  • 비용 효율화: 온프레미스 장비의 유지보수 비용을 줄이기 위해 클라우드 관리형 서비스(RDS 등)로 전환하는 경우.

마이그레이션 전략: 서비스 환경에 따른 선택

1. 오프라인 마이그레이션 (Offline Migration)

서비스를 일시적으로 중단하고 데이터를 한 번에 옮기는 방식입니다. 데이터 정합성 보장이 가장 확실하며 절차가 단순하지만, 중단 시간(Downtime)이 발생한다는 단점이 있습니다.

주요 단계: 서비스 중단 → 데이터 덤프(Export) → 데이터 복원(Import) → 검증 → 서비스 재개

#!/bin/bash
# 오프라인 데이터베이스 이전 자동화 스크립트 예시

# 변수 설정
SRC_HOST="legacy-db.internal"
DEST_HOST="new-cluster.cloud"
DB_NAME="service_production"
BACKUP_FILE="/tmp/db_migration_$(date +%Y%m%d).sql"

echo "[1/5] 어플리케이션 트래픽 차단 및 서비스 중지..."
# systemctl stop app-nodes

echo "[2/5] 소스 데이터베이스에서 데이터 추출 중..."
mysqldump -h $SRC_HOST -u admin -p \
    --single-transaction \
    --routines --triggers --events \
    $DB_NAME > $BACKUP_FILE

echo "[3/5] 타겟 데이터베이스로 데이터 로드 중..."
mysql -h $DEST_HOST -u admin -p $DB_NAME < $BACKUP_FILE

echo "[4/5] 데이터 정합성 검증..."
# checksum 또는 row count 비교 로직 실행
python3 verify_counts.py --src $SRC_HOST --dest $DEST_HOST

echo "[5/5] 엔드포인트 설정 업데이트 및 서비스 시작..."
# sed -i 's/legacy-db/new-cluster/g' /etc/app/config.yaml
# systemctl start app-nodes

echo "마이그레이션이 성공적으로 완료되었습니다."

2. 온라인 마이그레이션 (Zero-Downtime Migration)

서비스 중단 시간을 최소화하거나 아예 없애는 방식입니다. 소스 DB의 데이터를 실시간으로 타겟 DB에 복제(Replication)하면서 전환 시점에만 엔드포인트를 교체합니다.

핵심 매커니즘: 초기 스냅샷 전송 후 CDC(Change Data Capture) 기술을 이용하여 변경분(Incremental Data)을 지속적으로 동기화합니다.

import time
import logging

class MigrationOrchestrator:
    """무중단 데이터 마이그레이션 제어 클래스"""
    
    def __init__(self, src_conn, dest_conn):
        self.src = src_conn
        self.dest = dest_conn
        self.is_ready_to_cutover = False

    def start_process(self):
        try:
            # 1단계: 전체 데이터 스냅샷 동기화
            self._initial_full_sync()
            
            # 2단계: 실시간 증분 데이터 추적 및 동기화
            self._stream_incremental_changes()
            
            # 3단계: 정합성 체크 및 전환 대기
            if self._validate_integrity():
                self.is_ready_to_cutover = True
                print("전환 준비 완료: 지연 시간(Lag)이 임계치 미만입니다.")
                
        except Exception as e:
            logging.error(f"마이그레이션 중 오류 발생: {e}")
            self._rollback()

    def _stream_incremental_changes(self):
        """CDC 또는 Binlog 기반 복제 모니터링"""
        while not self.is_ready_to_cutover:
            replication_lag = self._get_sync_lag()
            print(f"현재 동기화 지연 시간: {replication_lag}초")
            
            # 지연 시간이 1초 미만일 때 전환 프로세스 진입 가능
            if replication_lag < 1:
                break
            time.sleep(5)

    def _get_sync_lag(self):
        # 타겟 DB에서 복제 상태 확인 쿼리 실행 (예: SHOW SLAVE STATUS)
        return 0.5 # 샘플 반환값

    def _validate_integrity(self):
        """핵심 테이블의 행 수 및 체크섬 비교"""
        print("데이터 검증 수행 중...")
        # 핵심 비즈니스 테이블 샘플링 검사 로직
        return True

    def _rollback(self):
        print("작업 취소 및 롤백 수행")

# 인스턴스 생성 및 실행
# orchestrator = MigrationOrchestrator(src_db_config, dest_db_config)
# orchestrator.start_process()

성공적인 이전을 위한 체크리스트

안전한 마이그레이션을 위해 실행 전 다음 사항을 반드시 점검해야 합니다.

  • 네트워크 대역폭: 데이터 전송 시 실시간 서비스의 네트워크 성능에 영향을 주지 않는지 확인합니다.
  • 권한 설정: 타겟 DB에 애플리케이션이 접근할 수 있는 적절한 IAM 역할 또는 유저 권한이 설정되었는지 확인합니다.
  • 파라미터 그룹: 소스와 타겟 DB 간의 Timezone, Character Set 등의 설정이 일치하는지 검증합니다.
  • 롤백 시나리오: 전환 후 예상치 못한 오류 발생 시 이전 데이터베이스로 즉시 되돌릴 수 있는 방안을 마련해 두어야 합니다.

태그: Database migration MySQL PostgreSQL Zero-Downtime

6월 16일 23:41에 게시됨