필사 모드: DB 마이그레이션 도구 2026 — Atlas / Flyway / Liquibase / Bytebase / pgroll / Squawk / gh-ost 심층 비교
한국어프롤로그 — "마이그레이션 한 줄로 5분 다운"
2026년 어느 팀의 사후 보고서.
> 23:14, 평소처럼 배포. `ALTER TABLE orders ADD COLUMN status_v2 VARCHAR(32) NOT NULL DEFAULT 'pending'`. 23:14:08, 프라이머리 락. 23:14:09, 모든 쿼리가 큐에 쌓이기 시작. 23:14:42, 백엔드가 connection pool 고갈, 5xx 시작. 23:19:31, 마이그레이션 종료. 23:19:34, 큐 소진, 정상화. **5분 22초 다운.** 테이블에는 4억 행이 있었다.
이게 2026년에도 흔한 풍경이다. 도구는 충분히 많다 — Flyway, Liquibase, Atlas, Bytebase, pgroll, Squawk, gh-ost, pt-osc, Reshape, dbmate, golang-migrate, Diesel, Alembic, Prisma Migrate, Drizzle Kit. 그런데 **"어떤 마이그레이션이 안전한가"를 도구가 알려주지 않으면**, 결국 5분 다운은 반복된다.
이 글은 2026년 DB 마이그레이션 도구의 지도를 그린다. 누가 무엇을 잘하고, 누가 무엇을 못 하고, 우리 팀은 무엇을 골라야 하는지까지.
1장 · 2026년 DB 마이그레이션 지도 — 선언적 vs 명령적 vs 온라인 변경
먼저 큰 그림. **모든 마이그레이션 도구는 세 축 중 어디에 서느냐**로 갈린다.
축 1: 선언적(declarative) vs 명령적(imperative)
- **명령적**: `ALTER TABLE`, `CREATE INDEX` 같은 SQL 스크립트를 사람이 직접 적고, 도구는 "어떤 스크립트를 어떤 순서로 돌렸는지"를 추적한다. Flyway·Liquibase·dbmate·golang-migrate·Alembic·Sqitch·Diesel·Prisma Migrate(기본).
- **선언적**: 사람은 "원하는 스키마 상태"만 적고, 도구가 현재 상태와 비교해서 diff를 계산해 ALTER 문을 생성한다. Atlas·Drizzle Kit·Prisma Migrate(prototype/db push)·Bytebase의 GitOps 모드.
선언적은 Terraform이 인프라에 한 일을 DB 스키마에 한다. 장점: 스키마가 코드로 한눈에 보인다. 단점: 도구가 만든 ALTER가 항상 안전하다는 보장이 없다(컬럼 rename을 drop+add로 풀어버리는 등).
축 2: 온라인(online) vs 오프라인(offline)
- **오프라인**: 마이그레이션 동안 테이블에 락이 걸리거나, 쿼리가 막힌다. 대부분의 전통적 도구의 기본.
- **온라인**: 라이브 트래픽이 도는 동안 스키마를 바꾼다. gh-ost·pt-osc·Reshape·pgroll. shadow 테이블 + trigger/logical replication으로 천천히 복사하면서 cutover.
작은 테이블(수십만 행 이하)은 오프라인이 단순하다. 큰 테이블(수억 행, 트래픽 많음)에서는 온라인이 필수.
축 3: forward-only vs reversible
- **reversible**: 모든 up 마이그레이션에 짝이 되는 down이 있어야 함. Flyway·Alembic·golang-migrate가 권장. 사고 시 롤백.
- **forward-only**: 롤백은 새 마이그레이션을 추가해서 한다. Sqitch·Atlas의 권장 모드. 이유: "실제로 4억 행 INSERT를 5분 만에 되돌릴 수 있는 down은 거의 없다 — 환상에 가깝다."
2026년 시니어들의 합의: **forward-only + 짧고 안전한 변경의 연속**이 reversible보다 현실적이다. down은 "방금 만든 빈 컬럼 drop" 정도까지만 의미가 있다.
한 장 요약
| 도구 | 모델 | 온라인? | 언어/대상 | 권장 사용처 |
| --- | --- | --- | --- | --- |
| Atlas | 선언적 | 직접 ❌, 통합 ✅ | 다중 DB | 선언적 스키마 + CI |
| Flyway 11 | 명령적 | ❌ | Java 친화 | 엔터프라이즈 표준 |
| Liquibase 4.30 | 명령적 + 메타 | ❌ | Java·XML/YAML | 대기업·다중 DB |
| Sqitch | 명령적 + DAG | ❌ | DB 중립 | 의존성 명시형 |
| Bytebase 3 | 둘 다 + 워크플로 | 통합 ✅ | 다중 DB | DB DevOps·리뷰 |
| Squawk | 린터 | n/a | Postgres | CI 게이트 |
| pgroll | 선언적 + expand/contract | ✅ | Postgres | 무중단 Postgres |
| gh-ost | online change | ✅ | MySQL | 대규모 MySQL |
| pt-osc | online change | ✅ | MySQL | 클래식 MySQL |
| Reshape | online change | ✅ | Postgres | Rust·무중단 |
| dbmate | 명령적 | ❌ | 다중 DB·Go CLI | 가벼운 CLI |
| golang-migrate | 명령적 | ❌ | Go·다중 DB | Go 표준 |
| Diesel | 명령적 + ORM | ❌ | Rust | Rust 친화 |
| Alembic | 명령적 + 오토젠 | ❌ | Python·SQLAlchemy | Python 표준 |
| Prisma Migrate | 양방향 | ❌ | TypeScript | Node 친화 |
| Drizzle Kit | 선언적 + 명령적 | ❌ | TypeScript | TS·경량 |
이게 2026년 지도. 이제 한 칸씩 들어가본다.
2장 · Expand-Contract 패턴 + 흔한 발자국들
도구를 고르기 전에 **패턴**을 먼저 알아야 한다. 도구가 좋아도 잘못된 패턴으로 쓰면 락이 걸린다.
Expand-Contract (Parallel Change)
무중단 마이그레이션의 핵심 패턴. **세 단계로 쪼갠다.**
1. **Expand**: 이전 + 새 스키마가 공존하도록 추가만 한다. drop 없음.
2. **Migrate**: 코드가 새 스키마로 쓰기/읽기를 함께. backfill로 과거 데이터를 새 스키마로 복사.
3. **Contract**: 모든 코드가 새 스키마만 쓰는 게 확인되면, 옛 스키마를 drop.
예시 — 컬럼 rename(`email` → `email_address`).
- 단순 rename은 위험: 옛 컬럼을 쓰는 배포 중인 코드가 즉시 깨진다.
- expand-contract: `email_address` 컬럼을 NULL 허용으로 추가 → 코드가 둘 다 쓰기 → backfill로 옛 데이터 복사 → 코드가 새 컬럼만 읽기 → 옛 컬럼 drop.
pgroll·Reshape·gh-ost가 이걸 기본 모델로 자동화한다. 다른 도구는 사람이 마이그레이션을 3개로 쪼개야 한다.
흔한 발자국 (foot-guns)
**1) NOT NULL 추가 (큰 테이블)**
Postgres 11+에서 `ALTER TABLE ... ADD COLUMN x INT NOT NULL DEFAULT 0`은 메타데이터만 바꾸고 끝(빠름). 하지만 `ALTER TABLE ... ALTER COLUMN x SET NOT NULL`은 전체 테이블을 스캔하면서 ACCESS EXCLUSIVE 락. 4억 행이면 분 단위로 락.
- 안전: `ALTER TABLE ... ADD CONSTRAINT x_not_null CHECK (x IS NOT NULL) NOT VALID` → `VALIDATE CONSTRAINT x_not_null` → 다음 배포에서 `SET NOT NULL`. NOT VALID는 락 없음.
- MySQL: 비슷한 패턴 + gh-ost/pt-osc.
**2) 컬럼 rename**
- 단순 `ALTER TABLE ... RENAME COLUMN`은 보통 메타데이터만 바꾸지만, **배포 중인 코드가 옛 이름을 참조 중이면 즉시 깨진다.**
- 안전: expand-contract (위 참조).
**3) 타입 변경 (INT → BIGINT)**
- Postgres: 보통 전체 테이블 다시 씀(rewrite). 큰 테이블은 락 분 단위.
- 안전: 새 컬럼을 BIGINT로 추가 → backfill → 코드 전환 → 옛 컬럼 drop.
**4) 인덱스 추가**
- Postgres: `CREATE INDEX CONCURRENTLY` (락 안 잡음). 마이그레이션 도구가 트랜잭션 안에서 돌리면 fail — 트랜잭션 밖에서 돌아야 함.
- MySQL: 작은 테이블은 즉시. 큰 테이블은 gh-ost/pt-osc.
**5) 디폴트 값 변경 (Postgres 11 이전 / 일부 타입)**
- Postgres 11 미만: 디폴트 추가는 전체 테이블 다시 씀. 11+에서는 메타데이터.
- 안전: 11+로 업그레이드. 또는 디폴트 없이 추가하고 backfill.
**6) Foreign key 추가**
- 락이 짧긴 하지만 양쪽 테이블에 락. 라이브 트래픽이 많으면 timeout 위험.
- 안전: `NOT VALID`로 추가 → 별도 트랜잭션에서 `VALIDATE CONSTRAINT`.
**7) 큰 DELETE / UPDATE**
- 한 트랜잭션 안에서 수백만 행 갱신 = WAL 폭증, replica lag, 락 보유.
- 안전: 배치(예: 1만 행씩, sleep 끼워서). 도구는 이걸 직접 안 해줌 — 별도 backfill 잡으로.
핵심: **마이그레이션 도구는 "스크립트를 순서대로 돌리는 무언가"일 뿐, 안전성을 보장하지 않는다.** 안전성은 위 패턴 + (Squawk 같은) 린터 + 리뷰에서 나온다.
3장 · Atlas (Ariga) — 선언적 스키마 관리의 대표
Atlas(ariga.io/atlas)는 2022년부터 시작해서 2024-2025년에 빠르게 점유율을 늘렸다. **HashiCorp의 Terraform을 DB 스키마에 한 도구.**
모델
`schema.hcl`(또는 SQL/JSON) 파일에 원하는 스키마를 적는다. Atlas가 현재 DB와 비교해서 diff를 계산하고 ALTER 문을 생성.
schema "public" {}
table "users" {
schema = schema.public
column "id" {
type = bigint
null = false
}
column "email" {
type = varchar(255)
null = false
}
primary_key { columns = [column.id] }
index "idx_users_email" {
columns = [column.email]
unique = true
}
}
CLI는 두 모드를 지원.
- **Declarative**: `atlas schema apply --to file://schema.hcl --url $DB_URL` — diff 계산해서 즉시 적용.
- **Versioned**: `atlas migrate diff add_user_email` — diff를 SQL 파일로 떨어뜨리고, 나중에 `atlas migrate apply`로 적용. 운영에서는 이쪽 권장.
강점
- **DB 중립**: Postgres·MySQL·SQLite·SQL Server·MariaDB·ClickHouse·MS SQL·Redshift 등.
- **CI 통합**: `atlas migrate lint`로 위험한 변경(NOT NULL 추가·DROP COLUMN·인덱스 누락) 자동 감지. GitHub Actions·GitLab CI 연동.
- **ORM 통합**: GORM·Ent·Hibernate·Prisma·Sequelize에서 스키마 추출해서 Atlas로 관리.
- **schema visualization**: `atlas schema inspect | atlas schema fmt` + ERD 생성.
약점
- 선언적 모델은 **컬럼 rename을 drop+add로 처리**하기 쉽다. Atlas는 `--diff-policy` 힌트로 막을 수 있지만 자동은 아님.
- 무중단(expand-contract)은 직접 지원 X — versioned 모드로 사람이 쪼개야 함.
- 무료 버전과 유료(Atlas Cloud, Pro) 기능 차이가 큼: 일부 lint·schema 모니터링·multi-tenant은 Pro 전용.
권장 사용처
- 새로 시작하는 Go 백엔드.
- Terraform 같은 IaC에 익숙한 팀.
- 다중 DB 환경에서 단일 도구로 통일하고 싶을 때.
2026년 기준 Atlas는 **선언적 진영의 사실상 표준** 위치.
4장 · Flyway 11 — Java 진영의 클래식
Flyway는 2010년부터, JBoss/Spring Boot 생태계의 표준. 2024년에 Redgate가 인수, 11.x는 모듈 정리와 새 데이터베이스 어댑터 중심.
모델
`V{version}__{description}.sql` 명명 규약. 단순 명령적.
db/migration/
V1__create_users.sql
V2__add_email_index.sql
V3__add_orders.sql
R__create_view_active_users.sql # Repeatable
U2__rollback_email_index.sql # Undo (Teams)
DB의 `flyway_schema_history` 테이블이 어떤 V가 돌았는지 추적. `flyway migrate`로 미적용분만 순서대로 적용.
강점
- **단순함**: SQL만 알면 끝. 새 팀원이 30분 안에 이해.
- **Spring Boot 자동 통합**: `spring-boot-starter-flyway` 한 줄.
- **DB 폭이 넓다**: Postgres·MySQL·Oracle·SQL Server·DB2·Sybase·MongoDB(Pro)·Snowflake·BigQuery 등 50+.
- **callback 훅**: `beforeMigrate`·`afterMigrate`·`beforeEachMigrate` 등 라이프사이클 훅.
- **baseline**: 이미 있는 DB에 Flyway 도입할 때 현재 상태를 baseline으로 마킹.
약점
- **rollback이 유료(Teams/Enterprise)**: OSS 버전은 forward-only. `Undo` 마이그레이션은 Teams 전용.
- **온라인 스키마 변경 미지원**: 큰 테이블은 gh-ost/pt-osc/pgroll과 조합 필요.
- **선언적 모드 없음**: 11.x에서 일부 기능(Auto-generate)이 들어왔지만 Atlas/Drizzle 수준의 선언은 아님.
- **린팅 약함**: 위험 패턴 자동 감지는 Squawk 같은 외부 도구에 의존.
권장 사용처
- Spring Boot/Java/Kotlin 백엔드.
- Oracle·DB2 같은 엔터프라이즈 DB.
- "SQL 그대로 보고 싶다, 추상화 적게 원한다"는 팀.
2026년에도 **JVM 진영의 디폴트**. 다만 새 프로젝트가 Flyway만 쓰는 일은 줄고, Atlas/Squawk와 조합하는 경우가 늘었다.
5장 · Liquibase 4.30 — XML/YAML 친화
Liquibase는 Flyway의 라이벌. 2006년부터, 4.30(2025-2026)은 changelog 포맷·DB 지원·UX 개선 중심.
모델
`changelog` 파일에 changeSet을 나열. 포맷은 XML·YAML·JSON·SQL 모두 가능.
databaseChangeLog:
- changeSet:
id: 1
author: alice
changes:
- createTable:
tableName: users
columns:
- column:
name: id
type: BIGINT
autoIncrement: true
constraints: { primaryKey: true, nullable: false }
- column:
name: email
type: VARCHAR(255)
constraints: { nullable: false, unique: true }
rollback:
- dropTable: { tableName: users }
강점
- **DB 추상화 강함**: 같은 changeSet이 Postgres·MySQL·Oracle에서 각 DB의 적절한 SQL로 변환. cross-DB 프로젝트에서 유리.
- **rollback이 OSS에서 1급 시민**: 각 change에 명시적 rollback 정의.
- **policy checks** (Pro): 위험 패턴 자동 차단.
- **GUI 도구**(Liquibase Hub, DBeaver 통합).
- **수많은 change 타입**: `addColumn`·`createIndex`·`addForeignKey`·`mergeColumns` 등 30+ 추상 change.
약점
- **XML/YAML 장황함**: SQL 1줄이 YAML 10줄이 되는 일.
- **추상화의 단점**: 추상 change가 생성하는 SQL이 항상 최적은 아님. 큰 테이블에서는 결국 raw SQL로 떨어뜨림.
- **선언적 모드 약함**: Liquibase는 명령적. 선언이 필요하면 별도.
- **OSS와 Pro 기능 차이 큼**: policy check·rollback의 일부 기능·Hub은 Pro.
Flyway vs Liquibase 한 줄
- Flyway: SQL 좋아하면.
- Liquibase: cross-DB·추상화·rollback 명시가 필요하면.
2026년 기준 둘은 거의 동률, 다만 신규 프로젝트는 Atlas로 옮겨가는 추세가 분명함.
6장 · Sqitch — 의존성 기반의 오래된 학파
Sqitch(sqitch.org)는 2012년에 David Wheeler가 만든, "Git 같은 마이그레이션 도구". **버전 번호가 아니라 의존성 그래프**로 마이그레이션을 추적.
모델
각 변경은 이름이 있는 change. `sqitch add appchanges`로 만들면 세 파일이 생김.
deploy/appchanges.sql -- 적용 SQL
revert/appchanges.sql -- 되돌리기 SQL
verify/appchanges.sql -- 검증 SELECT
`sqitch.plan`에 의존성을 명시.
add_users 2026-05-16T12:00:00Z alice
add_orders [add_users] 2026-05-16T13:00:00Z alice -- add_users에 의존
`sqitch deploy`는 의존성을 만족하는 순서로 적용. `sqitch verify`는 verify SQL을 돌려서 실제로 들어갔는지 확인.
강점
- **의존성 명시**: 마이그레이션이 100개 넘어가면 의존성 그래프가 진가를 발휘.
- **verify 1급 시민**: "마이그레이션이 적용됐다"와 "실제로 작동한다"를 구분.
- **DB 중립**: Postgres·MySQL·Oracle·SQLite·Snowflake·Firebird·Vertica·Exasol.
- **VCS 친화**: 브랜치별 마이그레이션 충돌이 적음(의존성 기반이라).
약점
- **러닝 커브**: 명령어가 많고(`deploy`·`revert`·`verify`·`bundle`·`tag`·`rework`), 다른 도구와 다른 멘탈 모델.
- **커뮤니티가 작음**: GitHub stars·StackOverflow 답변 수가 Flyway/Liquibase의 1/10 수준.
- **CI 통합·GUI는 약함**: 모든 게 CLI.
- **Perl 의존성**: 설치가 다른 도구보다 번거로움(Docker로 우회 가능).
권장 사용처
- 마이그레이션이 1000개 넘어가는 모놀리식 DB.
- "버전 번호 충돌"이 자주 나는 멀티 브랜치 환경.
- 의존성 그래프 + verify 모델을 좋아하는 팀.
2026년 Sqitch는 **틈새지만 충성도 높은** 도구. PostgreSQL의 일부 코어 컨트리뷰터들이 여전히 선호.
7장 · Bytebase 3 — DB DevOps + Review
Bytebase(bytebase.com)는 2021년 시작, 2025년 3.x로 점프. **GitLab/GitHub for DBs** 컨셉.
모델
웹 UI + GitOps. 마이그레이션 SQL을 PR로 올리면 Bytebase가:
1. **자동 lint** (SQL Review, Squawk-style 룰셋).
2. **승인 워크플로**(DBA가 리뷰).
3. **stage별 자동 배포**(dev → staging → prod).
4. **schema drift 감지**(Bytebase가 모르는 변경이 prod에 들어가면 알람).
5. **데이터 마스킹·access control**(누가 어떤 컬럼을 볼 수 있는지).
강점
- **사람 워크플로 1급**: 다른 도구는 CLI/CI에 그치는데 Bytebase는 사람이 리뷰하는 UI가 메인.
- **다중 DB·다중 환경**: 50+ DB 지원, dev/staging/prod 라이프사이클.
- **schema editor (GUI)**: 비개발자도 마이그레이션 제안 가능.
- **data masking**: PII 컬럼을 개발자에게 가리기.
- **자체 호스팅 OSS**: 자체 호스팅 무료(라이선스로 일부 기능 제한).
약점
- **인프라 부담**: Bytebase 자체가 서버 + DB. 작은 팀에는 오버킬.
- **벤더 종속**: GitOps 모드를 쓰면 다른 도구로 옮기기 어려움.
- **마이그레이션 자체는 다른 도구를 호출**: Bytebase가 직접 ALTER를 안 함, gh-ost/pt-osc/Flyway/Liquibase를 호출. orchestrator에 가까움.
권장 사용처
- DBA 팀이 있고, 개발자가 직접 prod DB에 접근하면 안 되는 환경.
- 50+ DB 인스턴스를 운영하는 큰 조직.
- "PR + 승인" 워크플로를 DB에도 적용하고 싶은 팀.
2026년 Bytebase는 **DB DevOps의 사실상 선두**. PlanetScale·Neon 같은 서버리스 DB가 자체 UI를 잘 만들어서 경쟁이 있지만, on-prem/multi-cloud는 Bytebase 우위.
8장 · Squawk — Postgres 마이그레이션 린터
Squawk(squawkhq.com)는 2020년에 Steve Dignam이 만든 작은 OSS 도구. **마이그레이션 SQL을 읽고 위험한 패턴을 잡는다.** 마이그레이션 도구가 아니라 린터.
모델
squawk migration.sql
위험한 패턴을 발견하면 에러/경고.
migration.sql:1:1: warning: adding-not-nullable-field
ALTER TABLE "user" ADD COLUMN "email" TEXT NOT NULL
Adding a NOT NULL field requires a table rewrite ...
Use ADD COLUMN ... DEFAULT NULL, backfill, then ALTER ... SET NOT NULL.
검출 룰 (Postgres 중심, 30+)
- `adding-not-nullable-field` — NOT NULL 추가 → 락.
- `changing-column-type` — 타입 변경 → rewrite.
- `disallowed-unique-constraint` — UNIQUE constraint 추가 → 전체 스캔, 락.
- `prefer-text-field` — VARCHAR(n) 대신 TEXT.
- `require-concurrent-index-creation` — `CREATE INDEX` (CONCURRENTLY 없이) → 락.
- `transaction-nesting` — 트랜잭션 안에서 `CREATE INDEX CONCURRENTLY` → fail.
- `renaming-column` — rename은 기존 코드를 즉시 깨뜨림.
- `renaming-table` — 위와 동일.
- `ban-drop-column` — DROP COLUMN은 expand-contract로.
- `constraint-missing-not-valid` — `ADD CONSTRAINT` (NOT VALID 없이) → 전체 스캔.
강점
- **CI에 한 줄**: `squawk **/V*.sql` — 모든 마이그레이션에 자동 게이트.
- **빠름**: Rust로 작성, 파일당 ms 수준.
- **GitHub Actions 댓글**: PR에 직접 코멘트로 위험을 알림.
- **마이그레이션 도구 중립**: Flyway·Liquibase·Atlas·dbmate 등 어느 도구의 SQL이든 lint 가능.
약점
- **Postgres 전용**: MySQL 룰은 거의 없음.
- **거짓 양성**: "이 테이블은 작으니 NOT NULL OK"를 모름. 룰별 disable로 회피.
- **데이터 안전성은 못 봄**: 락은 봐도 "이 컬럼 drop이 정말 안 쓰이는지"는 못 봄.
권장 사용처
- Postgres를 쓰는 모든 팀의 CI에 추가 (무료, 1줄).
- 큰 테이블이 있는 환경에서는 거의 필수.
2026년 시니어들의 합의: **Postgres 쓰면 Squawk는 디폴트.** 도구 선택과 무관하게 CI에 넣는다.
9장 · pgroll (Xata) — 무중단 Postgres 마이그레이션
pgroll(github.com/xataio/pgroll)은 Xata가 2023-2024년에 오픈소스로 공개. **expand-contract를 도구가 자동화한다**는 야심.
모델
JSON으로 마이그레이션을 정의. pgroll이 자동으로 view + trigger를 만들어서 옛 버전 + 새 버전 스키마를 동시에 노출.
{
"name": "rename_email_column",
"operations": [
{
"rename_column": {
"table": "users",
"column": "email",
"to": "email_address"
}
}
]
}
`pgroll start`로 시작 → 두 버전 공존(view로 노출) → `pgroll complete`로 옛 컬럼 제거. 중간에 `pgroll rollback`도 가능.
작동 원리
1. `pgroll start`가 새 컬럼/테이블/인덱스 추가 + view 생성.
2. 옛 view = 옛 스키마, 새 view = 새 스키마. 코드는 `SET search_path`로 어느 view를 쓸지 고름.
3. trigger가 양방향으로 데이터를 동기화(옛 컬럼 쓰면 새 컬럼에도, 새 컬럼 쓰면 옛 컬럼에도).
4. backfill이 백그라운드로 옛 데이터를 새 컬럼으로 복사.
5. 모든 트래픽이 새 view로 옮겨가면 `pgroll complete`로 옛 컬럼 drop.
강점
- **진짜 무중단**: 트래픽이 도는 동안 컬럼 rename·타입 변경·NOT NULL 추가가 됨.
- **rollback이 안전**: cutover 전에는 옛 view가 살아 있어서 즉시 복귀.
- **선언적 + expand-contract**: 사람이 3개 마이그레이션을 쪼갤 필요 없음.
- **Postgres 14+ 네이티브**: trigger·view·logical decoding을 활용. 외부 의존 없음.
약점
- **Postgres 전용**.
- **trigger 오버헤드**: 양방향 sync trigger가 쓰기 부하 증가.
- **사용 인구가 아직 적음**: 2026년에도 production 사용 사례는 토스·Notion·Xata 정도. 사례가 더 쌓여야 함.
- **모든 변경을 지원하지는 않음**: partition·복잡한 constraint는 아직.
권장 사용처
- Postgres 위에서 진짜 무중단이 필요한 서비스 (24/7 결제·메시징).
- 컬럼 rename·타입 변경이 잦은 빠르게 진화하는 스키마.
2026년 pgroll은 **Postgres 무중단 마이그레이션의 best bet**. 다만 안정성은 gh-ost(MySQL)에 비하면 아직 어림.
10장 · gh-ost / pt-online-schema-change / Reshape — 온라인 스키마 변경
큰 테이블 + 라이브 트래픽 = 온라인 스키마 변경 필요. 세 도구.
gh-ost (GitHub, MySQL)
2016년에 GitHub이 만들고 OSS화. **trigger 없이, binlog 기반으로 동작.**
작동:
1. shadow 테이블을 새 스키마로 만듦.
2. 옛 테이블의 행을 chunk 단위로 shadow에 INSERT.
3. binlog를 읽어서 진행 중 발생한 변경(INSERT/UPDATE/DELETE)을 shadow에도 적용.
4. cutover: 짧은 락으로 옛 테이블 ↔ shadow를 atomic swap.
gh-ost \
--user="root" --password="..." \
--host=primary.db \
--database="app" --table="orders" \
--alter="ADD COLUMN status_v2 VARCHAR(32) NOT NULL DEFAULT 'pending'" \
--execute
- 강점: trigger 없음 → 옛 테이블에 부하 적음. throttle(replica lag 보고 자동 감속) 좋음. interactive(`-cli`로 진행 중 일시정지·취소).
- 약점: MySQL/MariaDB 전용. binlog 읽기 권한 필요. foreign key 있는 테이블은 까다로움.
pt-online-schema-change (Percona, MySQL)
Percona Toolkit의 일부. **trigger 기반.**
작동:
1. shadow 테이블 + 옛 테이블에 INSERT/UPDATE/DELETE trigger 설치.
2. chunk 단위로 옛 → shadow 복사.
3. cutover: RENAME TABLE 두 번으로 swap.
- 강점: 오래되고 안정. gh-ost가 못 다루는 일부 케이스(주로 foreign key) 처리.
- 약점: trigger가 옛 테이블의 쓰기 부하를 늘림. throttle이 gh-ost보다 약함.
Reshape (Postgres, Rust)
2022년에 Fabian Lindfors가 만든 Rust 도구. **Postgres용 expand-contract.**
작동: pgroll과 비슷. SQL이 아니라 TOML로 마이그레이션 정의 + view + trigger로 옛/새 공존.
[[actions]]
type = "add_column"
table = "users"
column = "email_address"
data_type = "text"
nullable = true
- 강점: Rust로 빠름, 깔끔한 API.
- 약점: 메인테이너 활동이 2024년부터 줄었다는 평. 2026년 production 사용은 적음. pgroll이 사실상 추월.
셋의 비교
| | gh-ost | pt-osc | Reshape | pgroll |
| --- | --- | --- | --- | --- |
| DB | MySQL | MySQL | Postgres | Postgres |
| trigger | ❌ | ✅ | ✅ | ✅ |
| binlog | ✅ | ❌ | ❌ | ❌ |
| 활발도 (2026) | 중간 | 낮음 | 낮음 | 높음 |
| 권장 | MySQL 대규모 | gh-ost 안 되는 케이스 | (회피) | Postgres |
2026년 합의: **MySQL은 gh-ost가 디폴트**, fallback이 pt-osc. **Postgres는 pgroll**이 신흥 표준.
11장 · dbmate / golang-migrate / Diesel / Alembic — 언어별 옵션
각 언어 진영의 디폴트.
dbmate (Go, 다중 DB)
amacneil/dbmate. 한 파일 짜리 Go 바이너리, CLI 디자인이 깔끔.
dbmate new add_users
db/migrations/20260516120000_add_users.sql 생성
파일은 up/down 두 섹션.
-- migrate:up
CREATE TABLE users (id BIGINT PRIMARY KEY, email TEXT NOT NULL);
-- migrate:down
DROP TABLE users;
- 강점: Postgres·MySQL·SQLite·ClickHouse·BigQuery 지원. 의존성 0. shell-friendly. Docker 친화.
- 약점: 기능이 적음(lint·schema drift·rollback workflow는 없음). 그 단순함이 매력.
golang-migrate (Go, 다중 DB)
`golang-migrate/migrate`. CLI + Go 라이브러리.
migrations/
000001_create_users.up.sql
000001_create_users.down.sql
- 강점: Go 코드에서 라이브러리로 직접 호출 가능. CockroachDB·Spanner·MongoDB 어댑터.
- 약점: GitHub stars는 많은데 메인테이너가 줄었다는 평. issue 누적 중. 일부 어댑터는 maintenance 모드.
dbmate와 golang-migrate 사이 — 2024-2025년에 dbmate로 옮긴 팀이 늘었음. **새 Go 프로젝트는 dbmate 디폴트** (또는 Atlas).
Diesel migrations (Rust)
Diesel ORM의 일부. `diesel migration generate add_users` → `up.sql`/`down.sql`.
- 강점: Diesel과 통합 — schema.rs를 자동 생성, 컴파일 타임 타입 체크.
- 약점: Diesel 자체가 무거운 ORM. SQLx/sqlx::migrate를 선호하는 팀도 많음.
Alembic (Python, SQLAlchemy)
SQLAlchemy의 마이그레이션 도구. Python 코드로 마이그레이션을 정의.
def upgrade():
op.add_column('users', sa.Column('email', sa.String(255), nullable=False))
def downgrade():
op.drop_column('users', 'email')
- 강점: SQLAlchemy 모델에서 **autogenerate** — 모델 바꾸면 diff를 마이그레이션으로 자동 생성. Python 코드라 조건부 로직(`if dialect == 'postgresql':`) 가능.
- 약점: autogenerate가 컬럼 rename을 drop+add로 오해. Python 의존성 무거움. SQLAlchemy 안 쓰면 안 어울림.
2026년에도 **Python/Django는 Django migrations**, **Python/FastAPI/SQLAlchemy는 Alembic**이 디폴트.
12장 · Prisma Migrate / Drizzle Kit — TypeScript 진영
Node/TypeScript는 분열 상태. 둘이 양강.
Prisma Migrate
Prisma ORM의 일부. `schema.prisma`에 모델 적고, `prisma migrate dev`로 마이그레이션 생성/적용.
model User {
id BigInt @id @default(autoincrement())
email String @unique
}
- 강점: 스키마 → 마이그레이션 → 클라이언트 타입 한 번에. DX 압도적.
- 약점: 무거운 Rust 엔진. raw SQL 통제력 약함. 큰 테이블에 안전한 마이그레이션 생성을 보장 안 함(rename을 drop+add로). pgbouncer transaction 모드와 충돌.
Drizzle Kit
Drizzle ORM의 일부. 코드(TypeScript)로 스키마 정의 → `drizzle-kit generate`로 SQL diff 생성.
export const users = pgTable("users", {
id: bigserial("id").primaryKey(),
email: varchar("email", { length: 255 }).notNull().unique(),
});
- 강점: 가볍다(런타임 의존성 작음). raw SQL 가까이 — 생성된 SQL을 사람이 읽고 수정. edge runtime(Cloudflare Workers)에서 OK.
- 약점: 2026년에도 ecosystem이 Prisma보다 작음. 일부 어드밴스 기능(relations, complex types)은 Prisma가 강함.
2026년 TS 합의:
- **DX 최우선, 풀스택 Node** → Prisma.
- **edge·serverless·raw SQL 통제** → Drizzle.
- **대규모·무중단** → 둘 다 부족, 별도 마이그레이션 도구(Atlas/pgroll/gh-ost) 병행.
13장 · 누가 무엇을 골라야 하나 — 결정 트리
| 상황 | 추천 |
| --- | --- |
| 단일 앱, Postgres, 트래픽 적음 | dbmate 또는 golang-migrate + Squawk |
| 단일 앱, MySQL, 트래픽 적음 | Flyway 또는 dbmate |
| Java/Spring Boot | Flyway (또는 Liquibase) + Squawk |
| Node/TypeScript, 풀스택 | Prisma + Atlas(scale 시) |
| Node/TypeScript, edge | Drizzle Kit + Squawk |
| Python/Django | Django migrations |
| Python/FastAPI | Alembic |
| Rust | Diesel 또는 SQLx + dbmate |
| Go, 새 프로젝트 | Atlas (선언적) 또는 dbmate (단순) |
| 마이크로서비스 50+ | Bytebase + 각 서비스가 선택한 도구 |
| 대규모 Postgres, 무중단 필수 | pgroll + Squawk + Atlas |
| 대규모 MySQL, 무중단 필수 | gh-ost + Flyway + Squawk-MySQL 대체 |
| DBA 팀 있음, 리뷰 워크플로 | Bytebase 중심 |
| Cross-DB (Postgres + MySQL + Oracle) | Liquibase 또는 Bytebase |
| 마이그레이션 1000+ 누적 | Sqitch (의존성 그래프) |
흔한 안티패턴
- **"Prisma Migrate 하나로 production까지 다 해결"**: 작은 테이블이면 OK, 큰 테이블에 들어가면 lock으로 죽음. Squawk를 게이트로.
- **"Liquibase의 abstract change가 알아서 잘 해줄 것"**: cross-DB에서는 자주 suboptimal SQL 생성. raw SQL로 떨어뜨려서 확인.
- **"down 마이그레이션을 항상 작성"**: 4억 행 INSERT의 down이 진짜로 5분 안에 돌 거라 믿나? forward-only로 가는 게 현실.
- **"마이그레이션 도구가 안전을 보장"**: 어떤 도구도 NOT NULL을 안전하게 추가해주지 않는다. expand-contract + lint + 리뷰가 안전이다.
- **"gh-ost 한 번 돌리면 무조건 무중단"**: throttle 잘못 설정하면 replica lag 폭증. cutover 직전 락이 5초 이상이면 실패.
14장 · 한국 / 일본 사례 — 토스 / 카카오 / Mercari
토스 (Toss)
토스는 2023년 컨퍼런스(SLASH)에서 **gh-ost를 MySQL에서 광범위하게 사용**한다고 공개. 결제 도메인은 24/7이라 락은 곧 매출 손실. gh-ost로 chunk 크기·throttle을 도메인별로 튜닝.
2025-2026년 토스의 일부 신규 Postgres 서비스에서는 pgroll 검토 중이라는 발표. Squawk는 모든 신규 마이그레이션 PR의 필수 CI 단계.
카카오
카카오는 메신저·페이·뱅크 각각 DB 전략이 다름. **카카오뱅크는 Oracle 기반이라 Liquibase + 자체 워크플로**. 카카오 페이의 일부 신규 서비스는 Flyway + gh-ost(MySQL) 조합. 카카오 메신저는 자체 분산 storage라 일반 마이그레이션 도구 영역 밖.
2024년 카카오의 일부 팀이 Atlas를 도입해서 Go 백엔드를 통일했다는 이야기가 컨퍼런스에서 나왔음.
Mercari (일본)
Mercari는 micro-service 100+ 환경. **각 서비스가 도구 선택은 자율**, 다만 lint(Squawk·자체 룰)는 공통 CI. Spanner를 쓰는 서비스가 많아서 Spanner CLI + 자체 마이그레이션 프레임워크.
Mercari Engineering blog에서 "**Schema migration as code**" 컨셉을 자주 언급 — Atlas/Bytebase 비슷한 선언적 모델을 사내에서 구현.
LY Corporation (LINE + Yahoo Japan)
2023년 합병 후 LINE 코어 백엔드의 DB 마이그레이션은 Flyway 중심으로 표준화 중. 일부 신규 Go 서비스는 Atlas. **Squawk-MySQL 대체로 자체 룰을 추가한 sql-lint 도구**를 사내에서 운영.
공통 패턴:
- 큰 테이블엔 무조건 gh-ost (MySQL) / pgroll (Postgres).
- CI에 lint 게이트는 디폴트.
- DBA 리뷰가 prod 배포 게이트 (Bytebase든 자체 툴이든).
이게 2026년 한국·일본 빅테크의 흐름.
결론 — 도구는 많고, 패턴은 적다
긴 글을 한 장으로:
1. **도구는 충분히 많다.** Atlas, Flyway, Liquibase, Sqitch, Bytebase, pgroll, Squawk, gh-ost, pt-osc, Reshape, dbmate, golang-migrate, Diesel, Alembic, Prisma, Drizzle. 새로 하나 더 만들 필요 없음.
2. **선택은 언어/팀 친화도로 시작.** Java면 Flyway, Python이면 Alembic, Go면 Atlas/dbmate, TS면 Prisma/Drizzle.
3. **무중단이 필요하면 패턴이 도구를 결정**한다. expand-contract → pgroll(Postgres) / gh-ost(MySQL).
4. **Squawk(또는 동등 린터)는 디폴트**. Postgres 쓰는 한 CI에 무조건.
5. **포지티브 down은 환상.** forward-only + 짧고 안전한 변경의 연속이 reversible보다 현실.
6. **마이그레이션 도구는 안전을 보장 안 한다.** expand-contract 패턴 + 린트 + 리뷰가 안전.
마지막: **5분 다운**의 원인은 도구가 아니었다. `ALTER COLUMN ... SET NOT NULL`을 4억 행 테이블에 한 트랜잭션으로 돌린 패턴이 원인. 도구는 그대로 두고, 패턴을 expand-contract로 바꿨다면 5분이 0초가 됐을 것이다.
참고 / References
- Atlas: https://atlasgo.io/
- Atlas GitHub: https://github.com/ariga/atlas
- Atlas migrate lint: https://atlasgo.io/versioned/lint
- Flyway: https://flywaydb.org/
- Flyway 11 release notes: https://documentation.red-gate.com/fd/release-notes-for-flyway-engine
- Liquibase: https://www.liquibase.com/
- Liquibase 4.30 docs: https://docs.liquibase.com/
- Sqitch: https://sqitch.org/
- Sqitch tutorial: https://sqitch.org/docs/manual/sqitchtutorial-pg/
- Bytebase: https://www.bytebase.com/
- Bytebase docs: https://www.bytebase.com/docs/
- Bytebase GitHub: https://github.com/bytebase/bytebase
- Squawk: https://squawkhq.com/
- Squawk GitHub: https://github.com/sbdchd/squawk
- pgroll: https://github.com/xataio/pgroll
- pgroll docs: https://pgroll.com/docs
- gh-ost: https://github.com/github/gh-ost
- gh-ost cheatsheet: https://github.com/github/gh-ost/blob/master/doc/cheatsheet.md
- pt-online-schema-change: https://docs.percona.com/percona-toolkit/pt-online-schema-change.html
- Reshape: https://github.com/fabianlindfors/reshape
- dbmate: https://github.com/amacneil/dbmate
- golang-migrate: https://github.com/golang-migrate/migrate
- Diesel migrations: https://diesel.rs/guides/getting-started
- Alembic: https://alembic.sqlalchemy.org/
- Prisma Migrate: https://www.prisma.io/docs/concepts/components/prisma-migrate
- Drizzle Kit: https://orm.drizzle.team/kit-docs/overview
- GitHub Engineering — gh-ost: https://github.blog/2016-08-01-gh-ost-github-s-online-migration-tool-for-mysql/
- PlanetScale schema changes: https://planetscale.com/docs/concepts/branching
- Toss SLASH talks: https://toss.tech/slash
- Mercari Engineering blog: https://engineering.mercari.com/en/blog/
- Strong Migrations (Rails 컨텍스트): https://github.com/ankane/strong_migrations
- Expand and Contract pattern (Martin Fowler): https://martinfowler.com/bliki/ParallelChange.html
현재 단락 (1/411)
2026년 어느 팀의 사후 보고서.