(1/5) 내부망 MSSQL → AWS RDS for SQL Server 옮기기 — 부하 안 주고 그대로 넣는 법
📚 MSSQL → AWS RDS 마이그레이션 시리즈 (전체 5편)
내부망에 있던 MSSQL DB 한 덩어리를 AWS RDS for SQL Server 로 그대로 옮겨야 했어요. 다행히 양쪽은 S2S VPN 으로 이미 통신이 되는 상태였고, 조건은 두 가지였어요. 첫째 원본을 그대로 옮길 것(스키마/식별자/인덱스 보존), 둘째 운영 중인 원본 DB에 부하를 주지 말 것. 이 글은 그때 검토했던 방법들과 최종 선택, 그리고 건수별 소요 시간 추정을 정리한 글이에요.
💡 이 글에서 다루는 것
- 내부망 → AWS RDS for SQL Server 마이그레이션 방법 6가지 비교
- “부하 방지” 관점에서 각 방법이 원본에 주는 영향
- 1만 건 / 100만 건 / 1억 건 기준 예상 소요 시간
- 최종 추천: 어떤 상황엔 무엇을 골라야 하는가
1. 전제 — 우리 환경 정리
먼저 작업 시작 전에 짚어둔 전제예요.
| 항목 | 값 |
|---|---|
| 원본 | 내부망 MSSQL (온프레미스, SQL Server 2019 기준) |
| 대상 | AWS RDS for SQL Server (같은 메이저 버전 또는 상위) |
| 네트워크 | S2S VPN 으로 양방향 통신 가능 (대역폭은 보통 1Gbps 미만) |
| 목표 | 원본 그대로 복제 (one-shot 풀 카피) |
| 제약 | 원본 DB 부하 최소화, 서비스는 운영 중 |
여기서 가장 중요한 두 가지는 “그대로” 와 “부하 없이” 예요. 이 두 단어가 방법 선택의 거의 모든 기준이 돼요.
2. 옵션 정리 — 6가지 방법
먼저 후보를 다 펼쳐놓고 시작할게요. 표 한 번 보고 본문으로 들어가요.
| # | 방법 | 그대로 보존 | 원본 부하 | 네트워크 부담 | 작업 난이도 |
|---|---|---|---|---|---|
| 1 | Native Backup / Restore (.bak + S3) | ⭐⭐⭐⭐⭐ | ⭐ (낮음) | 한 번에 큰 파일 | 낮음 |
| 2 | AWS DMS (Full Load) | ⭐⭐⭐ | ⭐⭐ | 균등 분산 | 중간 |
| 3 | bcp (Bulk Copy) | ⭐⭐⭐⭐ | ⭐⭐ | 테이블 단위 | 중간 |
| 4 | SSIS (Integration Services) | ⭐⭐⭐⭐ | ⭐⭐⭐ | 균등 분산 | 중간~높음 |
| 5 | SqlPackage (BACPAC) | ⭐⭐⭐⭐ | ⭐⭐ | 한 번에 큰 파일 | 낮음 |
| 6 | Linked Server INSERT … SELECT |
⭐⭐ | ⭐⭐⭐⭐⭐ (높음) | 가장 비효율 | 가장 낮음 |
⚠️ 표의 별 개수 의미 — “보존” 은 많을수록 좋음(원본 그대로), “부하” 와 “네트워크 부담” 은 많을수록 나쁨(원본/네트워크에 부담). 별 1개가 좋은 것일 수도, 나쁜 것일 수도 있어요. 헷갈리지 않게 ⭐ 옆에 (낮음)/(높음) 으로도 표시했어요.
3. 방법 1 — Native Backup/Restore (.bak → S3 → RDS)
MSSQL 의 정공법. 원본에서 BACKUP DATABASE 로 .bak 파일을 만들고, S3 에 올린 다음, RDS 의 저장 프로시저 msdb.dbo.rds_restore_database 로 복원하는 방법이에요.
-- 원본 (내부망 MSSQL)
BACKUP DATABASE [MyDB]
TO DISK = N'D:\backup\MyDB_full.bak'
WITH COMPRESSION, CHECKSUM, STATS = 10;
# S3 업로드 (내부망 jump 서버에서 → S2S VPN 통해 S3 endpoint)
aws s3 cp D:\backup\MyDB_full.bak s3://my-mssql-migration/MyDB_full.bak
-- AWS RDS for SQL Server 에서 실행
EXEC msdb.dbo.rds_restore_database
@restore_db_name = 'MyDB',
@s3_arn_to_restore_from = 'arn:aws:s3:::my-mssql-migration/MyDB_full.bak';
진행상황은 EXEC msdb.dbo.rds_task_status @db_name='MyDB'; 로 따라갈 수 있어요.
장점
- 스키마/인덱스/제약/식별자 시드/CLR 어셈블리 전부 그대로 보존돼요. “그대로 넣는다” 의 끝판왕.
- 원본 부하는 풀백업 시점의 디스크 I/O 만. row-by-row 쿼리가 없으니 운영 쿼리에 영향이 작아요.
- 큰 DB 도 압축 백업이면 보통 1/4~1/8 로 줄어들어 전송도 빨라요.
단점
- RDS 가 Native Backup/Restore 옵션 그룹 활성화 + S3 권한이 사전에 세팅되어 있어야 해요.
- AWS RDS for SQL Server 에서
differential복원은 일정 버전 이상부터 지원돼요. 풀백업 + 차등은 미리 RDS 버전 확인. - 한 파일이 큼 → 전송 중 끊기면 처음부터 다시 (멀티파트 업로드로 어느 정도 보완 가능).
✅ “그대로” 가 가장 잘 지켜지는 방법. one-shot 마이그레이션이라면 1순위 후보.
4. 방법 2 — AWS DMS (Database Migration Service)
AWS 가 만든 마이그레이션 전용 매니지드 서비스. Replication Instance 를 띄우고, 소스(내부망 MSSQL) / 타깃(RDS for SQL Server) 엔드포인트를 등록한 뒤, Full Load 또는 Full Load + CDC 로 돌려요.
[내부망 MSSQL] --(S2S VPN)--> [DMS Replication Instance] --> [AWS RDS for SQL Server]
장점
- 테이블/스키마 단위로 병렬 로드 가능. 큰 DB 를 시간 안에 끝내고 싶을 때 좋아요.
- CDC(Change Data Capture) 를 켜면 풀 로드 후 변경분만 따라잡아 줘서 다운타임 거의 0 컷오버 가능.
- 진행률/에러/지표가 콘솔에 다 보임 → 운영하기 편함.
단점
- 보존 측면에서 인덱스/식별자 시드/제약을 별도 챙겨야 해요. DMS 는 데이터 위주로 옮기고, 보조 인덱스는 옵션으로 따로 만들거나 컷오버 후 생성하는 게 일반적이에요. “그대로” 라는 관점에선 백업/복원만 못해요.
- 풀로드 도중 원본에 장기 SELECT 가 걸려요. 트랜잭션 격리 수준/락 옵션을 잘못 두면 오히려 원본에 부담.
- DMS Replication Instance 비용이 따로 들어요.
💡 “운영 중인 DB 를 무중단에 가깝게 옮기고 싶다” 가 강한 요건이면 DMS + CDC 가 최선.
5. 방법 3 — bcp (Bulk Copy Program)
MSSQL 의 고전 명령어. 테이블 단위로 원본에서 파일로 추출(bcp out)하고, 대상에 일괄 적재(bcp in 또는 BULK INSERT)해요.
# 원본에서 추출 (네이티브 포맷이 가장 빠르고 안전)
bcp MyDB.dbo.Orders out Orders.dat ^
-S 192.168.x.x -U sa -P <PASSWORD> -n -b 10000
# AWS RDS 로 적재
bcp MyDB.dbo.Orders in Orders.dat ^
-S mydb.xxxx.ap-northeast-2.rds.amazonaws.com -U admin -P <PASSWORD> -n -b 10000 -h "TABLOCK"
옵션 의미는 짧게.
-n: 네이티브(바이너리) 포맷. 파싱 비용이 적어서 빠르고 타입 오차도 없음.-b 10000: 배치 사이즈. 트랜잭션을 잘게 끊어서 로그 폭주를 막아요.-h "TABLOCK": 대상 테이블에 락을 잡고 적재 → minimally logged operation 으로 빨라져요.
장점
- 테이블별로 골라서 옮길 수 있음. “이 테이블만 빨리” 가 가능.
- 배치 사이즈 / 병렬 처리(여러 bcp 동시 실행) 로 부하 조절 이 비교적 쉬워요.
- AWS 서비스 의존성 없음. 사내 jump 서버에서 그냥 돌리면 끝.
단점
- 풀 DB 옮기려면 테이블/제약/인덱스 스크립트는 별도로 준비해야 함.
- 외래키/식별자 시드/순서 의존성을 직접 관리해야 함 (적재 순서, IDENTITY_INSERT 등).
⚠️ bcp 에서 부하 방지의 핵심은
-b배치 사이즈 와 동시 실행 수 둘이에요. 무작정-b 0으로 한 트랜잭션에 다 밀어넣지 마세요. 트랜잭션 로그가 폭주합니다.
6. 방법 4 — SSIS / 방법 5 — SqlPackage(BACPAC)
두 개는 묶어서 짧게.
SSIS (SQL Server Integration Services)
- ETL GUI 가 익숙하다면 좋아요. 패키지 한 번 만들어두면 재실행도 편함.
- 내부적으로는 결국 bulk insert + 데이터 흐름이라 속도는 bcp 와 비슷한 급.
- 단점: 패키지를 어디서 돌릴지(SSIS 카탈로그 서버) 가 필요. 일회성 마이그레이션엔 좀 무거워요.
SqlPackage (BACPAC / DACPAC)
SqlPackage.exe /Action:Export ...→.bacpac파일 생성 → S3 업로드 → 대상에 Import.- 스키마 + 데이터를 한 덩어리로 들고 다니기에 좋아요. 작은 DB 에 잘 맞음.
- 큰 DB(수십 GB 이상)는 내보내기/들여오기 둘 다 느려서
.bak보다 불리해요.
이 둘은 원본 부하 관점에선 bcp 와 비슷하거나 살짝 더 무거워요(스키마 메타데이터 추출이 추가됨).
7. 방법 6 — Linked Server INSERT ... SELECT (비추)
가장 쉬워 보이지만, 실제론 운영 환경에서 거의 안 쓰는 방법.
-- 대상(RDS) 에서 원본을 Linked Server 로 등록 후
INSERT INTO MyDB.dbo.Orders
SELECT * FROM [SRC_LINK].[MyDB].[dbo].[Orders];
왜 비추인가
- 원본에서 한 줄 한 줄 네트워크로 끌어옴. S2S VPN 왕복 지연이 매 row 마다 발생해요.
- 원본 트랜잭션 로그/락 부담이 큼. 부하 방지가 요건인데 정반대로 가요.
- 도중에 끊기면 일관성 회복도 까다로움.
🚨 이 방법은 테스트용 작은 테이블 외엔 쓰지 마세요. 100만 건만 돼도 시간이 끝없이 늘어집니다.
8. 추천 — 우리 케이스에서의 베스트
요건이 “그대로” + “부하 최소” + “S2S VPN 있음” 이라면 결론은 명확해요.
🎉 1순위 — Native Backup/Restore (.bak → S3 → RDS)
이유.
- 원본에서 발생하는 부하는 백업 작업 한 번뿐. 그것도 디스크 I/O 라 운영 쿼리(메모리/CPU 위주)와 겹치는 영역이 작아요.
- 풀백업 시점에 원본을 점적(point-in-time) 으로 떠오므로 데이터 일관성 보장.
- 인덱스/제약/IDENTITY/통계까지 그대로 복원. 사용자 요건 그대로.
🎉 2순위 — DMS Full Load + CDC (다운타임 거의 0 이 필요한 경우)
- 컷오버 시간을 분 단위로 줄여야 한다면 DMS.
- 단, “그대로” 가 아니라 “내용은 같지만 인덱스 등은 새로 만들어도 됨” 이 허용돼야 가성비가 좋아요.
🎉 3순위 — bcp (특정 테이블만 빠르게)
- “이 테이블 하나만 옮기면 돼요” 같은 부분 마이그레이션에 최적.
✅ 사내 케이스에서 저는 1순위(Native Backup/Restore) 로 풀카피 → 컷오버 시점에 차등 백업 한 번 더 로 정리하는 패턴을 가장 선호해요.
9. 소요 시간 추정 — 1만 건 / 100만 건 / 1억 건
이 부분은 환경마다 차이가 큰 영역이에요. 그래도 기준선이 있어야 계획이 가능하니, 평균 행 크기 1KB, 인덱스 보통 수준, S2S VPN 실효 200~500Mbps 가정으로 정리할게요.
9-1. 방법별 처리량(throughput) 감
| 방법 | 평균 처리량 (1KB 행 기준) | 비고 |
|---|---|---|
| Native Backup/Restore | 네트워크 대역폭에 수렴 | row 단위 처리량 개념이 아님 |
| AWS DMS (Full Load, 단일 task) | ~5,000 ~ 20,000 rows/sec | 병렬 task 로 N배 확장 |
| bcp (네이티브 + TABLOCK + 적절한 배치) | ~20,000 ~ 100,000 rows/sec | LAN 이면 더 빨라짐 |
| SSIS | ~10,000 ~ 50,000 rows/sec | 패키지 튜닝 의존 |
| BACPAC (Import) | ~3,000 ~ 15,000 rows/sec | 인덱스 재생성 비용 큼 |
| Linked Server | ~100 ~ 1,000 rows/sec | WAN 왕복 지연으로 매우 느림 |
9-2. 건수별 예상 시간
⚠️ 아래 숫자는 단일 task / 단일 connection 기준 추정치예요. 실제로는 병렬화로 단축돼요.
| 방법 | 1만 건 | 100만 건 | 1억 건 |
|---|---|---|---|
| Native Backup/Restore | 의미 없음 (한 번에 처리) | 약 1~3분 (.bak 1~5GB) | 약 30분~2시간 (.bak 100~500GB, 압축 시) |
| AWS DMS (단일) | ~1~3초 | ~1~3분 | ~3~10시간 (병렬 시 1/N) |
| bcp (튜닝 후) | <1초 | ~10~50초 | ~30분~2시간 |
| SSIS | ~1초 | ~30초~2분 | ~1~3시간 |
| BACPAC Import | ~2~5초 | ~1~5분 | ~3~10시간 |
| Linked Server | ~10~60초 | ~30분~3시간 | ❌ 사실상 비현실적 |
핵심 포인트
- 1만 건 수준은 어떤 방법이든 1분 안에 끝남. 시간 차이가 거의 없음.
- 100만 건부터는 방법별로 분~시간 단위 차이가 벌어져요. bcp 와 .bak 가 압도적으로 빠름.
- 1억 건 이상은 단일 task 로 끌고 가지 말고, 테이블/파티션 단위 병렬화 가 필수.
💡 정확한 수치는 결국 행 크기, 인덱스 개수, 트랜잭션 로그 설정, 네트워크 실효 대역폭에 좌우돼요. 본격 작업 전엔 샘플 테이블 100만 건으로 한 번 실측 해두는 걸 추천해요.
10. 부하 방지 체크리스트
방법을 골랐다면, 실행 단계에서 챙겨야 할 부하 방지 포인트들이에요.
- 작업 시간대를 운영 트래픽 적은 시간 으로 잡기 (보통 새벽 2~5시)
- 원본 쿼리는 가능하면
READ UNCOMMITTED또는 스냅샷 격리 로 잡아서 락 충돌 회피 - bcp/DMS 사용 시 배치 사이즈 를 작게 시작해서 점진적으로 키우기
- 트랜잭션 로그 백업 주기 짧게 유지 (단순 복구 모델이면 더 좋음)
- Resource Governor 로 마이그레이션 세션 CPU/IO 상한 걸기
- 백업 파일은 원본 디스크와 다른 볼륨 에 떨어뜨리기 (IO 분리)
- 작업 중 원본 DB 모니터링 대시보드 띄워두기 (CPU, IO, lock waits)
11. 정리 — 어떤 상황엔 무엇
마지막으로 상황별 추천을 한 번 더 정리해드릴게요.
| 상황 | 추천 방법 |
|---|---|
| 운영 중지 시간을 1~2시간 줄 수 있다 + 그대로 옮기고 싶다 | Native Backup/Restore |
| 다운타임을 거의 0 으로 가야 한다 | DMS Full Load + CDC |
| 특정 큰 테이블 몇 개만 빠르게 옮기면 된다 | bcp |
| DB 가 작고(수 GB) 한 덩어리로 들고 다니고 싶다 | BACPAC |
| GUI ETL 익숙하고 재실행 자주 한다 | SSIS |
| Linked Server INSERT…SELECT | 하지 마세요 |
저희 사내 케이스처럼 “그대로” + “부하 최소” + “S2S 가능” 이면 Native Backup/Restore 가 거의 항상 1순위예요. DMS 는 컷오버 단축이 필요할 때 더해서 쓰는 식.
일단 오늘은 여기까지…..
다음 글에서는 실제로 Native Backup/Restore 를 RDS 옵션 그룹 세팅부터 끝까지 따라가는 실전편을 정리해볼게요.