date
slug
author
status
tags(최대 3개)
summary
type
thumbnail
category
updatedAt
0. 개요
MySQL Native Online DDL은 Inplace/Instant 알고리즘을 지원합니다. 어떤 명령은 즉시 끝나지만, 어떤 것은 아주 오래 걸리며 문제를 일으키기도 합니다. 문제의 대부분은 메타데이터 잠금(MDL)이 쓰기나 읽기 쿼리를 차단(blocking)하는 현상입니다.
여기엔 제가 오래 품어온 궁금증, 일종의 기술 부채가 있었습니다. Inplace/Instant Online DDL과 pt-online-schema-change 중 어느 쪽이 MDL 리스크가 더 적은가? 이 글은 그 의문을 풀고, 워크로드가 높은 테이블의 구조를 변경할 때 어떤 도구를 선택해야 할지 정리합니다.
1. MySQL Online DDL 알고리즘: INSTANT와 INPLACE
MySQL 8.0 이상 기준으로 Online DDL은 세 가지 알고리즘(INSTANT, INPLACE, COPY)을 제공합니다. 이 중 서비스 영향을 최소화하려고 주로 쓰는 INSTANT와 INPLACE의 실행 흐름을 보겠습니다.
INSTANT
테이블 데이터에 접근하지 않고 메타데이터만 변경하는 방식입니다. 컬럼 추가 등 일부 작업에서만 쓸 수 있습니다.
ALTER TABLE 시작 ➡ 메타데이터 변경 (Data Dictionary만 업데이트) ➡ 완료
테이블 크기와 무관하게 수 밀리초 안에 끝나며, 서비스에 미치는 영향이 사실상 적습니다. (정말 그럴까요? 🤔)
INPLACE
테이블 데이터를 별도 작업 공간에서 직접 변경하되, 진행 중에도 DML을 허용하는 방식입니다. 인덱스 추가/삭제, 컬럼 타입 변경 등 대부분의 DDL에서 쓰입니다.
Preparation 단계: ALTER TABLE 시작 ➡ Shared Upgradable MDL 획득 ➡ 테이블 정의 검증, 임시 구조 준비 Execution 단계: ➡ 인덱스 빌드 또는 데이터 재구성 ➡ 그동안 유입된 DML은 메모리에 저장 ➡ 데이터(테이블) 크기에 비례해 작업 시간 소요 Commit 단계: ➡ Exclusive MDL로 업그레이드 ➡ 메타데이터 최종 반영 ➡ Exclusive MDL 해제
핵심은 시작과 끝 두 시점에서 잠금 전환이 일어난다는 점입니다. 특히 마지막 Commit 단계에서 Exclusive MDL로 업그레이드할 때 심각한 차단이 발생할 수 있습니다.
INPLACE의 두 모드: 테이블 리빌드 vs 인덱스 빌드(리빌드 없음)
같은 INPLACE라도 내부 동작은 크게 둘로 나뉩니다. 아래 코드가 이를 명확히 구분합니다.
// storage/innobase/handler/handler0alter.cc (mysql-8.0.28) ... /** Operations for creating secondary indexes (no rebuild needed) */ static const Alter_inplace_info::HA_ALTER_FLAGS INNOBASE_ONLINE_CREATE = Alter_inplace_info::ADD_INDEX | Alter_inplace_info::ADD_UNIQUE_INDEX | Alter_inplace_info::ADD_SPATIAL_INDEX; ... /** Operations that InnoDB cares about and can perform without rebuild */ static const Alter_inplace_info::HA_ALTER_FLAGS INNOBASE_ALTER_NOREBUILD = INNOBASE_ONLINE_CREATE | INNOBASE_FOREIGN_OPERATIONS | Alter_inplace_info::DROP_INDEX | Alter_inplace_info::DROP_UNIQUE_INDEX | Alter_inplace_info::RENAME_INDEX | Alter_inplace_info::ALTER_COLUMN_NAME | Alter_inplace_info::ALTER_COLUMN_EQUAL_PACK_LENGTH | Alter_inplace_info::ALTER_INDEX_COMMENT | Alter_inplace_info::ADD_VIRTUAL_COLUMN | Alter_inplace_info::DROP_VIRTUAL_COLUMN | Alter_inplace_info::ALTER_VIRTUAL_COLUMN_ORDER | Alter_inplace_info::ALTER_COLUMN_INDEX_LENGTH; ...
리빌드가 필요 없는 작업(
INNOBASE_ONLINE_CREATE, INNOBASE_ALTER_NOREBUILD)인 인덱스 삭제, 이름 변경 등은 테이블 리빌드 없이 메타데이터만 수정합니다. 인덱스 생성도 여기 포함됩니다.테이블 리빌드가 필요한 작업은 내부적으로 다음 과정을 거칩니다.
임시 테이블 파일 생성 ➡ 데이터 복사 & 구조 변경 (Execution 단계) ➡ 데이터 변경 병합 & 원본과 교체 (Commit 단계)
이 원본 테이블과 구조 교체 작업은 InnoDB 엔진 레벨의 file operation이며, pt-osc가 쓰는 RENAME TABLE과는 다른 메커니즘입니다.
인덱스 빌드와 테이블 리빌드의 차이는 DDL 실행 결과로 확인할 수 있습니다.
ALTER TABLE t ADD INDEX idx(col), ALGORITHM=INPLACE, LOCK=NONE; -- Query OK, 0 rows affected (30.52 sec) -- Records: 0 Duplicates: 0 Warnings: 0 ← 인덱스 빌드만 ALTER TABLE t MODIFY COLUMN c BIGINT, ALGORITHM=INPLACE, LOCK=NONE; -- Query OK, 0 rows affected (45.12 sec) -- Records: 10000000 Duplicates: 0 Warnings: 0 ← 리빌드 발생
어느 모드든 Commit 단계에서 Exclusive MDL이 발생합니다. 차이는 이 단계에서 소비하는 리소스와 시간입니다.
하나의 ALTER 문에 여러 작업을 묶는다면?
ALTER TABLE t ADD COLUMN c INT, -- INSTANT 가능 DROP INDEX uk_old, -- INPLACE 필요 ADD UNIQUE INDEX uk_new(); -- INPLACE 필요
하나의 ALTER에 여러 operation이 섞이면 가장 제한적인 알고리즘이 선택됩니다. 위 예는 컬럼 추가가 INSTANT로 가능하더라도 인덱스 변경이 INPLACE를 요구하므로, 전체가 INPLACE로 수행되어 INSTANT의 이점을 잃습니다.
워크로드가 높은 테이블에 INSTANT로 가능한 작업이 있다면, 따로 분리해 테이블 리빌드를 피하는 편이 유리할 수 있습니다.
2. 메타데이터 잠금의 종류
MySQL의 MDL에는 여러 유형이 있는데, DDL에서 중요한 것은 다음과 같습니다.

잠금 유형 간 호환성이 중요합니다. X(Exclusive) 요청이 큐에 있으면 새 SR이나 SW가 grant되지 않습니다. 즉 모든 읽기/쓰기 쿼리 차단이 발생합니다.
3. INSTANT/INPLACE의 메타데이터 잠금 양상
INSTANT DDL의 MDL
테이블 리빌드 없이 매우 빠르게 실행되고 DML을 병합하지 않으므로, Commit 단계의 Exclusive MDL이 아주 짧습니다. Shared Upgradable에서 Exclusive로의 업그레이드 요청도 매우 빠르게 이뤄집니다.
그러나 서비스에 영향을 주는 시나리오가 있습니다.
- Long-running 트랜잭션: INSTANT가 아무리 빨라도 Exclusive 업그레이드 시점에 Shared MDL 보유자가 있으면 대기해야 합니다. 이때 심각한 blocking chain이 발생할 수 있습니다. Bug #106480에서 보고됐듯, 미완료 트랜잭션이 있는 상태에서 컬럼을 추가하면 그 트랜잭션이 끝날 때까지 blocking이 일어납니다. MySQL 개발자도 이를 expected behaviour 로 확인했고, 초기 공식 문서의 No exclusive metadata locks are taken 설명이 잘못됐음을 인정해 문서가 수정됐습니다.
- 파티션이 많은 테이블: Bug #83435 보고처럼 파티션 수가 많으면 Commit 단계의 내부 처리 비용이 커져 Exclusive MDL이 비정상적으로 길어질 수 있습니다. Bug #94610에서는 이 문제가 InnoDB의
dict_sysmutex를 장시간 점유하며, 대상과 무관한 다른 테이블의 DML까지 차단하는 현상으로 이어졌습니다.
이런 사례를 보면 INSTANT DDL도 프로덕션에서 주의가 필요합니다.
INPLACE DDL의 MDL
Preparation 단계
- SU(Shared Upgradable) 획득 시도
- SR(SELECT)와 호환 → 읽기 허용
- SW(DML)와 호환 → 쓰기 허용
- 다른 SU/X와는 비호환 → 동시에 다른 DDL 불가
- 테이블 메타데이터 스냅샷, 새 인덱스/임시 테이블 구조 준비
- 이 단계에서 열린 장시간 트랜잭션이 있으면 SU 획득 자체가 대기 가능 (lock_wait_timeout 적용)
Execution 단계
- SU를 유지한 채 장시간 작업 수행 (인덱스 빌드 또는 테이블 리빌드)
- DML/SELECT 전부 정상 진행 (SR/SW가 SU와 호환)
- 이 기간의 모든 DML은 online alter log에 기록됨 (
row_log_online_op())
Commit 단계
- SU → X(Exclusive)로 업그레이드 요청
- 기존 SR/SW 홀더가 전부 끝나야 X 획득 가능 — long-running 트랜잭션 하나가 전체 서비스를 막는 지점
- X 획득 후: ① online alter log에 쌓인 DML replay (
row_log_apply()→ InnoDB 내부 B-tree 연산으로 직접 적용) ② Data dictionary 업데이트(이때dict_sys->mutex점유) ③ Table definition cache 무효화(구 버전 제거)
- X 해제 → pending queue의 SR/SW가 일제히 grant됨 (thundering herd)
여기서 주의할 점은 online alter log의 replay가 X lock 보유 중에 이뤄진다는 것입니다. 공식 문서의
innodb_online_alter_log_max_size 설명도 extends the period of time at the end of the DDL operation when the table is locked to apply the data from the log 라고 명시합니다.Online alter log의 Replay 구조
InnoDB는 SQL 문장이 아니라 변경된 row의 물리적 데이터(operation type, index key value, row data)를 alter log에 기록합니다.
이 log replay는 SQL을 재실행하는 방식이 아닙니다. Commit 단계에서
row_log(_table)_apply가 각 entry의 operation type에 따라 InnoDB 내부 B-tree 연산 함수(row_ins_clust_index_entry 등)를 직접 호출해 INSERT/UPDATE/DELETE를 적용합니다. SQL 파서, 옵티마이저, 핸들러 인터페이스를 전혀 거치지 않는 엔진 내부 연산이라 개별 entry 처리 속도가 빠릅니다.일반 SQL보다 빠를 뿐입니다. Execution 단계에서 DML이 많이 발생할수록 alter log가 커지고, 그만큼 X lock 보유 시간이 길어집니다. 커밋 단계가 수 밀리초라는 설명은 DML이 적을 때만 성립하며, 워크로드가 높은 테이블에서는 log replay 시간만큼 X lock이 유지되어 서비스 영향이 커집니다.
왜 모든 ALTER TABLE은 반드시 Exclusive MDL을 획득하는가?
MySQL은 Data dictionary와 Table definition cache에서 테이블 구조 상태가 다른 것(다중 버전)을 허용하지 않습니다. DDL이 끝나면 기존 정의를 무효화하고 새 정의로 교체해야 하는데, 이 작업 중 다른 세션이 구 정의를 참조하면 불일치가 생깁니다. 이를 막으려고 cache를 무효화하는 순간 반드시 Exclusive MDL로 다른 세션의 접근을 차단합니다.
CREATE INDEX 문도 내부적으로 ALTER TABLE로 매핑되어 같은 단계를 따릅니다. 즉 secondary index 추가도 Preparation → Execution → Commit(Exclusive MDL) 단계를 거칩니다.
4. Exclusive 메타데이터 잠금의 영향
모든 메타데이터 잠금 요청을 차단
앞의 메타데이터 잠금의 종류에서 언급했듯, Exclusive MDL은 모든 Shared MDL뿐 아니라 또 다른 Exclusive MDL 요청까지 차단합니다.
MySQL은 MDL 요청에 대해 granted queue와 pending queue를 관리합니다. Exclusive MDL을 요청하면 pending queue에 들어가고, 이때부터 blocking chain이 시작됩니다(끔찍합니다). 아래 규칙 때문입니다.
Pending queue에 X가 있으면 새로운 SR/SW 등을 모두 grant하지 않는다.
MDL 유형별 호환 규칙 때문이기도 하고, grant 코드에도 명시되어 있습니다.
// sql/mdl.cc (mysql-8.0.28) ... bool MDL_lock::can_grant_lock(enum_mdl_type type_arg, const MDL_context *requestor_ctx) const { bool can_grant = false; bitmap_t waiting_incompat_map = incompatible_waiting_types_bitmap()[type_arg]; bitmap_t granted_incompat_map = incompatible_granted_types_bitmap()[type_arg]; /* New lock request can be satisfied iff: - There are no incompatible types of satisfied requests in other contexts - There are no waiting requests which have higher priority than this request. */ if (!(m_waiting.bitmap() & waiting_incompat_map)) { // ... granted queue 체크로 진입 ...
핵심은 주석의 There are no waiting requests which have higher priority than this request 조건입니다.
m_waiting.bitmap은 현재 pending queue에 있는 lock type의 bitmap이고, waiting_incompat_map은 요청한 type과 비호환인 pending type의 bitmap입니다. X는 SR/SW의 비호환 대상에 포함되므로, pending X 하나만으로도 이후 들어오는 모든 SR/SW가 이 if 조건에서 막혀 granted queue 체크 단계로 내려가지도 못합니다.예시 시나리오는 이렇습니다.

(1) DDL이 X lock 요청 → pending queue 진입 — A, B, C, D는 이미 granted라 계속 실행되고, DDL은 이 넷이 끝나길 대기합니다.

(2) 새 SELECT(E), INSERT(F) 도착 — 새 요청이 모두 DDL 뒤 pending에 갇힙니다.

(3) 새 쿼리 계속 도착, pending 증가 — A, C, D가 끝나고 B만 남았는데, 새 요청이 대규모로 pending에 쌓입니다.

(4) DDL이 X grant 후 실행 완료 — B 종료 후 DDL이 X를 획득해 실행하고 해제하면, 그제야 후속 요청들이 granted되어 실행됩니다.
이처럼 Exclusive MDL은 pending queue에서 기다리기만 해도 수많은 세션의 후속 MDL 요청을 차단합니다.
다른 테이블 쿼리까지 차단할 수 있다
한 가지 더 주의할 점이 있습니다. Online DDL Commit 단계에서 DDL 대상이 아닌 다른 테이블 쿼리까지 대기하는 현상이 나타날 때가 있습니다.
MDL은 테이블 단위이므로 테이블 A에 X lock이 걸려도 테이블 B의 MDL에는 영향이 없어야 하는데, 왜 이럴까요? MDL이 아니라 InnoDB 내부의
dict_sys mutex 때문입니다. Data dictionary를 업데이트할 때 InnoDB는 dict_sys->mutex를 잡는데, 이 mutex는 테이블 단위가 아니라 글로벌 범위입니다. 이게 점유된 동안 다른 세션이 새 테이블을 열거나, 임시 테이블을 생성하거나, dictionary에 접근하는 모든 작업이 대기합니다.이 mutex 대기 때문에 대용량·대규모 환경의 테이블 작업 시 SHOW PROCESSLIST 명령조차 응답이 지연되는 일을 겪을 수 있습니다.
보통은 이 mutex 보유 시간이 매우 짧아 체감되지 않지만, online alter log replay가 길어지거나 파티션 테이블에서 각 파티션의 dictionary 작업이 반복되면 점유가 비정상적으로 길어질 수 있습니다. 즉 DDL 대상 테이블만 영향을 받는다는 건 MDL 관점에서만 맞고, InnoDB 내부에서는
dict_sys mutex를 통해 서버 전체에 영향을 줄 수 있다는 점을 알아둬야 합니다. (최근 버전에서는 이 글로벌 병목이 해결됐을까요? 🤔)INPLACE 인덱스 생성의 특수성
앞서 INPLACE에 인덱스 빌드와 테이블 리빌드 두 모드가 있다고 했습니다. Secondary 인덱스 생성은 테이블을 재구성하는 INPLACE에 비해 Commit 단계가 상대적으로 가볍습니다.
- Execution 단계 자체가 짧음 — 새 인덱스 B-tree 빌드만 하므로 테이블 전체 데이터 복사가 없고, 데이터 scan을 병렬로 수행하며, 그만큼 쌓이는 online alter log 양이 적음
- Commit 단계의 Data dictionary 업데이트가 단순 — 인덱스 메타데이터 추가만 하면 됨 (테이블 리빌드는 파일 레벨 전환을 수반)
다만 테이블 재구성에 비해 가벼울 뿐, Commit 단계가 안전하다는 뜻은 아닙니다. Exclusive MDL을 요청하고 차단을 일으키는 양상은 동일합니다.
참고: PostgreSQL의 CREATE INDEX CONCURRENTLY와 비교
MySQL은 메타데이터 잠금 큐에서 강력한 잠금 모드(Exclusive MDL)를 존중해 새 SR/SW를 모두 차단하는 구조입니다. PostgreSQL의
CREATE INDEX CONCURRENTLY는 근본적으로 다른 접근을 취합니다.Phase 1: 카탈로그 등록
- ShareUpdateExclusiveLock 획득 (DML 전부 허용, 다른 DDL만 차단)
- 카탈로그에 인덱스 등록,
indisvalid = false
- 커밋 → lock 해제
- WaitForLockers: 이 시점 스냅샷에 있던 트랜잭션만 대기 (새 트랜잭션은 차단 없음)
Phase 2: 첫 번째 스캔 — 인덱스 빌드
- ShareUpdateExclusiveLock 재획득 (DML 여전히 허용)
- 테이블을 스캔하며 인덱스 빌드
- 커밋 → lock 해제
- WaitForLockers: Phase 2 시작 시점 스냅샷만 대기
Phase 3: 두 번째 스캔 — 변경분 반영
- ShareUpdateExclusiveLock 재획득
- Phase 2 중 발생한 DML을 인덱스에 반영, 검증 후
indisvalid = true
- 커밋 → 인덱스 사용 가능
- 전 단계에 걸쳐 SELECT/DML이 한 번도 차단되지 않음
핵심은
WaitForLockers의 동작 방식입니다. 이 시점 이전에 시작된 활성 트랜잭션 목록을 스냅샷으로 찍고, 그 목록의 트랜잭션이 끝나길 기다리며, 대기 중 새로 시작되는 트랜잭션은 완전히 무관해 차단이 없고, 목록이 비면 다음 phase로 진행합니다.트랜잭션 스냅샷(virtual trx id) 기반으로 대기하므로 lock queue에 영향을 주지 않고, 새 트랜잭션의 lock 획득을 방해하지도 않습니다.
5. INPLACE DDL vs pt-osc: 메타데이터 잠금 비교
pt-osc 각 단계의 MDL
pt-online-schema-change는 원본 테이블의 빈 복사본을 만들고, 트리거로 변경분을 동기화하면서 데이터를 옮긴 뒤 테이블을 스왑하는 방식입니다. MDL이 필요한 시점은 크게 네 곳입니다.
1단계: CREATE TRIGGER (×3)
- SNW(Shared No Write) 획득 시도
- SELECT(SR)와 호환 → 읽기 허용
- DML(SW)와 비호환 → 쓰기 차단
- 기존 SW 홀더가 끝나길 대기 (lock_wait_timeout 내 획득 실패 시 트리거 생성 실패로 전체 작업 롤백)
- 트리거 생성 완료 후 SNW 해제, 이후 일반 DML은 다시 진행 가능. 단, 이 시점부터 원본 DML이 트리거를 통해 신규 테이블로 전파됨
2단계: 테이블 복사
INSERT … SELECT로 chunk 단위 복사 진행
- 원본 테이블에
SELECT … LOCK IN SHARE MODE로 SR 획득
- 신규(작업) 테이블에
INSERT INTO …로 SW 획득
- chunk 단위로 SR/SW가 잡혔다 풀렸다 반복되므로 장시간 보유되는 MDL은 없음
- 동시에 사용자 DML도 원본에 SW로 진입 가능하고, 트리거 실행으로 신규 테이블에도 SW를 동반 획득 (SW끼리 호환이라 복사 SW와 공존)
3단계: RENAME TABLE (테이블 스왑)
RENAME TABLE t TO _t_old, _new_t TO t를 원자적 단일 구문으로 실행
- 두 테이블 모두에 Exclusive(X) MDL 획득
- SELECT/DML/다른 DDL 모두 차단 — pt-osc의 유일한 전역 blocking 구간
- 보유 시간은 매우 짧지만(메타데이터 교체만 수행), 기존 장시간 트랜잭션이 원본의 SR/SW를 들고 있으면 X가 pending queue에 들어가 앞서 설명한 blocking chain을 유발
- 스왑 완료 즉시 X 해제
4단계: DROP TRIGGER (×3)
- 이제
_t_old가 된 구 원본 테이블의 트리거 삭제
DROP TRIGGER도 X MDL을 잠깐 획득
- 차단 대상이 이미 rename되어 접근 경로에서 사라진
_t_old뿐이라 서비스 영향은 사실상 없음
pt-osc 장점 1: X lock 보유 시간의 예측 가능성
pt-osc도 시작과 끝 시점에 MDL을 획득하고, 특히 끝 단계에 Exclusive MDL을 요구합니다.

가장 큰 장점은 Exclusive MDL 보유 시간이 워크로드와 무관하게 일정하다는 점입니다.
트리거가 DML을 실시간으로 새 테이블에 반영하므로 마지막 단계에 밀린 데이터 변경분이라는 개념이 없습니다. RENAME 시점에만 짧은 Exclusive MDL을 요청합니다. 반면 INPLACE DDL은 평소 DML 트래픽과 파티션 여부에 따라 Exclusive MDL 보유 시간이 길어지고, 다른 테이블 액세스에까지 영향을 줄 수 있습니다.
pt-osc 장점 2: 재시도 가능성
INPLACE DDL은 Commit 단계에서 Exclusive MDL 획득에 실패할 수 있고(Preparation 단계에서도 lock timeout이 가능합니다), 실패하면 지금까지 진행한 테이블 재구성이 수포로 돌아갑니다. 반면 pt-osc는 MDL 획득 실패 시 운영적 유연성에서 유리합니다.
pt-online-schema-change \ --alter "..." \ --set-vars lock_wait_timeout=2 \ --tries create_triggers:10:1,drop_triggers:10:1,swap_tables:10:1 \ ...
위처럼 lock timeout을 2초로 두고, MDL을 요구하는 각 작업의 재시도 횟수/간격을 지정할 수 있습니다. 또한 트리거 생성 단계는 SR(SELECT)을 차단하지 않는 SNW MDL을 획득하므로 시작 단계의 영향도가 상대적으로 더 적습니다.
6. 결론: 어떤 DDL 도구를 선택할 것인가?
모든 DDL을 pt-osc로 실행해야 할까?
pt-osc가 MDL 영향 면에서 리스크가 적은 건 분명합니다. Exclusive MDL 보유 시간이 RENAME 한 번의 수 ms로 고정되고, 그 시간이 DML 워크로드와 무관하게 예측 가능하다는 점은 큰 장점입니다. 하지만 그 대가가 가볍지 않습니다.
- 속도: INSTANT가 수 ms~수 초, INPLACE가 분 단위로 끝낼 작업을 pt-osc는 테이블 크기에 비례해 시간 단위로 처리합니다. chunk 단위
INSERT … SELECT로 전체 테이블을 복사하므로 구조적으로 느립니다.
- 디스크 공간: 원본과 같은 크기의 shadow table이 작업 내내 유지되어, 테이블 크기만큼의 여유 공간이 추가로 필요합니다. 수백 GB 규모 테이블에선 이 조건 자체가 제약입니다.
- 트리거 오버헤드: 작업이 끝날 때까지 원본의 모든 DML이 트리거를 통해 shadow table로 동시에 쓰이므로 write latency가 평시보다 늘고, replication lag이 누적되는 원인이 되기도 합니다.
- Foreign key 처리의 까다로움: FK가 걸린 테이블은
-alter-foreign-keys-method옵션으로 재구성 방식을 명시해야 하고, 방식마다 고유한 주의점이 있습니다.
- 중단 시 정리 부담: 비정상 종료되면 shadow table과 트리거가 남아 수동으로 정리해야 합니다.
그럼에도 정말 중요한 서비스의, 단 한순간도 트랜잭션이 끊기지 않는 바쁜 테이블이라면 저는 pt-osc를 준비하겠습니다.
INSTANT와 INPLACE를 고려해야 할 이유
INSTANT DDL은 컬럼 추가/삭제처럼 실무에서 가장 빈번한 구조 변경을 메타데이터 수정만으로 거의 즉시 처리합니다. 앞서 살펴본 long transaction과 many-partitions 케이스만 의식하고 트래픽이 낮은 시간대를 고르면, pt-osc의 비용을 치르지 않고도 충분히 안전하게 적용할 수 있습니다.
INPLACE DDL도 마찬가지입니다. 테이블을 재구성하지 않고 즉시 끝나는 명령(인덱스 삭제, DEFAULT 값 변경 등)도 있고, 특히 일반적인 secondary 인덱스 생성은 pt-osc와 큰 차이를 보일 만큼 빠릅니다.
초당 수백~수천 건이 발생하는 무거운 워크로드 테이블이 아니라면, 서비스 피크를 피해 INPLACE와 INSTANT를 먼저 고려해 보는 게 어떨까 하는 게 개인적인 의견입니다.
모든 테이블이 다 무겁고 바쁘지는 않다
현대의 데이터베이스 시스템은 대부분 마이크로서비스로 잘게 쪼개져 있고, 캐시와 이벤트 큐로 DB 부하를 상당히 덜어내는 아키텍처로 구성됩니다. 정말 주의해야 할 무거운 워크로드 지점은 시스템 전체에서 일부분이며, 모든 테이블이 대용량에 대규모 액세스를 받는 건 아닙니다.
중요한 것은 프로덕션 데이터베이스의 부하를 잘 관측하는 것입니다.
테이블 레벨 통계를 잘 수집하고 모니터링해, 어떤 테이블이 DDL 방식 선택에 주의해야 할 뜨거운 테이블인지, 아니면 INSTANT/INPLACE로 충분한지를 데이터로 판단할 수 있는 가시성 기반을 갖추는 것이 중요합니다.

