ALTER TABLE 같은 스키마 변경이 테이블을 잠그는 현상. "마이그레이션 돌렸더니 서비스가 멈췄다"의 정체다.
무엇이 위험한가
ALTER TABLE 이 ACCESS EXCLUSIVE 락을 잡는다
- 읽기조차 막힌다
- 그동안 커넥션이 쌓인다
- 커넥션 풀이 마른다
- DB 를 쓰는 모든 기능이 멈춘다
테이블 하나를 고쳤는데 서비스 전체가 내려간다
더 무서운 것 — 락 대기 줄
-
① 긴 SELECT 가 돌고 있다 (약한 락 보유)
-
② ALTER TABLE 이 들어와 ACCESS EXCLUSIVE 를 기다린다
-
③ 그 뒤의 모든 쿼리가 ②를 앞지르지 못하고 함께 대기한다
-
ALTER 자체는 1초면 끝날 작업인데, 앞의 긴 쿼리 때문에 전체가 멈춘다
대기 중인 DDL이 뒤의 모든 쿼리를 막는다는 점이 핵심이다. 그래서 타임아웃이 필수다.
-- PostgreSQL: 락을 3초 이상 못 잡으면 포기한다 (뒤를 막지 않는다)
SET lock_timeout = '3s';
ALTER TABLE member ADD COLUMN nickname VARCHAR(50);
-- 실패하면 재시도한다
안전한 변경과 위험한 변경
안전 (PostgreSQL 11+ · 즉시 끝난다)
- DEFAULT 있는 칼럼 추가 ← 11부터 테이블 재작성 없이 된다
- 칼럼 이름 변경
- 인덱스 CONCURRENTLY 생성/삭제
위험 (테이블 전체를 다시 쓴다)
- 칼럼 타입 변경 (VARCHAR(50) → VARCHAR(100) 은 예외적으로 안전)
- NOT NULL 추가 (전체 검사)
- 큰 테이블에 인덱스를 CONCURRENTLY 없이 생성
안전하게 하는 방법
-- PostgreSQL: 인덱스는 반드시 CONCURRENTLY
CREATE INDEX CONCURRENTLY idx_member_email ON member (email);
-- (트랜잭션 안에서 못 쓴다. 실패하면 INVALID 인덱스가 남으니 확인 후 재생성)
-- NOT NULL 은 두 단계로
ALTER TABLE member ADD CONSTRAINT chk_email_nn CHECK (email IS NOT NULL) NOT VALID;
ALTER TABLE member VALIDATE CONSTRAINT chk_email_nn; -- 약한 락
-- MySQL 8.0: 알고리즘을 명시해 재작성 여부를 강제한다
ALTER TABLE member ADD COLUMN nickname VARCHAR(50), ALGORITHM=INPLACE, LOCK=NONE;
-- LOCK=NONE 이 불가능한 변경이면 즉시 에러 → 모르고 서비스를 멈추는 일을 막는다
ALGORITHM=INPLACE, LOCK=NONE을 항상 붙이는 습관이 실무의 안전장치다. 안 되는 변경이면 실행 전에 알려 준다.
대안 도구
gh-ost · pt-online-schema-change (MySQL)
- 새 테이블을 만들고 트리거·binlog 로 따라잡은 뒤 이름을 바꾼다
- 락 시간을 순간으로 줄인다
면접 함정
- ❌ "칼럼 추가는 가벼운 작업" → 버전과 옵션에 따라 테이블 전체 재작성이 될 수 있다.
- ❌ "DDL은 트랜잭션이 안 된다" → PostgreSQL은 DDL도 트랜잭션 안에서 롤백된다(MySQL은 암묵적 커밋).