Skip to content

서비스 중단 없이 DB 스키마를 변경하는 실습 프로젝트입니다.

Notifications You must be signed in to change notification settings

songsunkook/db-migration-test

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

DB Schema Migration Without Downtime

서비스 중단 없이 DB 스키마를 변경하는 실습 프로젝트입니다.

🎯 프로젝트 목적

기술 블로그 글: "서비스 중단 없이 DB 스키마 변경하기"의 실습 예제

실무에서 발생하는 다음과 같은 문제들을 해결하는 방법을 학습합니다:

  • ALTER TABLE로 인한 서비스 장애
  • 스키마 변경과 애플리케이션 배포 시점의 불일치
  • 대용량 테이블의 무중단 스키마 변경

🏗️ 실습 환경

  • Backend: Spring Boot 3.x + JPA
  • Database: MySQL 8.0
  • Feature Flag: Togglz
  • Required: Docker

🚀 Quick Start

1. 환경 시작

# MySQL 환경 시작
./scripts/start.sh

# 애플리케이션 실행
./gradlew bootRun

2. 웹 인터페이스 접속

3. API 테스트

# 사용자 목록 조회
curl http://localhost:8080/api/users

# Feature Flag 상태 확인 (4단계부터 사용)
curl http://localhost:8080/api/feature-flags

📝 실습 시나리오

현재 상황

users 테이블에서 first_name, last_name을 개별 관리 중

목표

full_name 컬럼으로 데이터 이전 후 기존 컬럼 제거

🌿 브랜치별 실습

각 단계별로 브랜치를 구성하여 점진적 배포 과정을 시뮬레이션합니다:

main (초기 상태)
├── step1-expand (스키마 확장)
├── step2-dual-write (Dual Write 구현)  
├── step3-backfill (데이터 마이그레이션)
├── step4-read-conversion (읽기 전환)
├── step5-cleanup (코드 정리)
└── step6-contract (스키마 축소)

실습 방법

  1. 각 단계별 브랜치 체크아웃하여 해당 단계의 코드와 변경사항 확인
  2. **migration-notes/current-step.md**에서 현재 단계 상태 및 수행 작업 확인
  3. 미리 생성된 PR을 통해 단계별 변경사항과 실제 배포 과정 검토

📚 실습 단계

0단계: 초기 상태 확인

테이블 구조 확인

-- MySQL 접속
mysql -h localhost -P 3306 -u root -p0000 test_db

-- 테이블 구조 확인 (기존 스키마)
DESCRIBE users;

API 테스트

# 사용자 생성 (기존 스키마)
curl -X POST http://localhost:8080/api/users \
  -H "Content-Type: application/json" \
  -d '{"firstName":"John","lastName":"Doe","email":"john.doe@example.com"}'

# 생성된 사용자 조회
curl http://localhost:8080/api/users/1

1단계: Expand - 스키마 변경 대상 추가 (DDL)

스키마 확장만 하고 기존 스키마는 유지합니다. 신규 컬럼은 아직 NULL 상태입니다.

-- 서비스 중단 없이 새 컬럼 추가
ALTER TABLE users 
ADD COLUMN full_name VARCHAR(255) NULL,
ALGORITHM=INSTANT, LOCK=NONE;

-- 변경 확인
DESCRIBE users;

2단계: Dual Write - 기존/신규 스키마 쓰기 대응 (APP)

애플리케이션이 이미 Dual Write로 구현되어 있습니다. 기존/신규 스키마 양쪽에 모두 쓰기 작업을 수행합니다.

# 새 사용자 생성 (full_name도 함께 저장됨)
curl -X POST http://localhost:8080/api/users \
  -H "Content-Type: application/json" \
  -d '{"firstName":"Jane","lastName":"Smith","email":"jane.smith@example.com"}'

# Dual Write 확인
curl http://localhost:8080/api/users/2

현재 코드에서 UserService.createUser() 메소드가 Dual Write를 자동으로 수행합니다:

  • firstName, lastName (기존 스키마)
  • fullName (신규 스키마)

3단계: Back Fill - 기존 데이터 마이그레이션 (BATCH)

기존 데이터를 신규 스키마로 마이그레이션합니다.

-- 기존 데이터에 full_name 채우기
UPDATE users 
SET full_name = CONCAT(first_name, ' ', last_name) 
WHERE full_name IS NULL;

-- 백필 결과 확인
SELECT id, first_name, last_name, full_name FROM users;

4단계: Read Conversion - 읽기 전환 (APP)

Feature Flag로 신규 스키마 읽기를 점진적으로 전환합니다. Dual Write는 유지됩니다.

# 신규 스키마 읽기 전환 활성화
curl -X PUT http://localhost:8080/api/feature-flags/new-schema-read \
  -H "Content-Type: application/json" \
  -d '{"enabled":true}'

# 점진적 배포: 50% 사용자에게만 적용
curl -X PUT http://localhost:8080/api/feature-flags/percentage-rollout \
  -H "Content-Type: application/json" \
  -d '{"percentage":50}'

# 읽기 방식 확인
curl http://localhost:8080/api/feature-flags/user/1/status
curl http://localhost:8080/api/users/1/display-name

5단계: Clean Up - 코드 정리 (APP)

  • Feature Flag 제거
  • Dual Write 제거 (신규 스키마만 사용)
  • 기존 스키마 관련 코드 제거
# 100% 사용자에게 신규 스키마 적용
curl -X PUT http://localhost:8080/api/feature-flags/percentage-rollout \
  -H "Content-Type: application/json" \
  -d '{"percentage":100}'

6단계: Contract - 기존 스키마 대상 제거 (DDL)

애플리케이션이 더이상 기존 스키마를 참조하지 않은 후 기존 스키마를 제거합니다.

-- full_name을 NOT NULL로 변경
ALTER TABLE users MODIFY COLUMN full_name VARCHAR(255) NOT NULL;

-- 기존 컬럼 제거 (Clean Up 완료 후 실행)
ALTER TABLE users DROP COLUMN first_name;
ALTER TABLE users DROP COLUMN last_name;

-- 인덱스 추가
CREATE INDEX idx_users_full_name ON users(full_name);

📚 참고 자료

🤝 Contributing

실습 중 발견한 이슈나 개선 사항이 있다면 Issue를 등록해 주세요.

About

서비스 중단 없이 DB 스키마를 변경하는 실습 프로젝트입니다.

Resources

Stars

Watchers

Forks