앱을 운영하면서 스키마 바꾸기
컬럼 이름을 바로 바꾸면 아직 교체되지 않은 앱 인스턴스가 옛 이름을 조회하다 실패할 수 있다. 큰 테이블의 변경은 오래 걸리거나 다른 요청을 잠금 대기로 세울 수도 있다.
이 장에서 처음 나오는 말3개
DDLData Definition Language- 테이블·인덱스 같은 객체의 정의를 만드는 SQL.
migration스키마 마이그레이션- DB 구조 변경을 순서와 이력으로 관리하는 작업.
backfill- 새 컬럼이나 구조에 기존 데이터를 채워 넣는 작업.
추가하고 이행한 뒤 제거하기
섹션 제목: “추가하고 이행한 뒤 제거하기”작업 제목을 새 구조로 옮긴다는 가정에서 순서를 나누어 본다.
| 단계 | DB 작업 | 앱이 만족할 조건 |
|---|---|---|
| Expand | 새 컬럼·테이블을 추가 | 이전 앱도 계속 동작 |
| 이행 | 기존 값 채우기, 필요 시 양쪽 쓰기 | 새 값 누락·불일치를 관찰 |
| 전환 | 새 구조로 읽기 변경 | 구버전 요청·배치가 남았는지 확인 |
| Contract | 옛 구조 제거 | 되돌릴 때 필요한 데이터와 절차 확보 |
실제 변경은 더 단순할 수도 있다. 모든 변경에 두 컬럼을 유지할 필요는 없지만, 동시에 실행되는 앱 버전과 데이터의 상태를 먼저 생각하는 순서는 유효하다.
잠금을 무한정 기다리지 않기
섹션 제목: “잠금을 무한정 기다리지 않기”공통 예제의 로컬 DB에서 실행한다. 새 컬럼을 확인한 뒤 취소한다.
BEGIN;SET LOCAL lock_timeout = '2s';ALTER TABLE public.tasks ADD COLUMN priority integer;SELECT column_nameFROM information_schema.columnsWHERE table_schema = 'public' AND table_name = 'tasks' AND column_name = 'priority';ROLLBACK;트랜잭션 안에서 priority가 한 행 보이고 종료 후에는 없다.
빠른 메타데이터 변경도 잠금을 획득해야 하므로 오래 열린 트랜잭션 뒤에서 기다릴 수 있다.
변경 종류별 잠금과 테이블 재작성 여부를 확인한다.
(ALTER TABLE)
인덱스 생성의 운영 차이
섹션 제목: “인덱스 생성의 운영 차이”일반 CREATE INDEX는 작업 중 해당 테이블의 쓰기를 막을 수 있다.
CREATE INDEX CONCURRENTLY는 쓰기를 허용하며 생성하지만 작업 단계와 대기가 늘고,
실패하면 유효하지 않은 인덱스가 남을 수 있다. 트랜잭션 블록 안에서 실행할 수 없다.
(CREATE INDEX)
로컬의 자동 커밋 상태에서 실행하고 정리한다.
CREATE INDEX CONCURRENTLY tasks_project_idx ON public.tasks (project_id);SELECT indexrelid::regclass, indisvalidFROM pg_indexWHERE indexrelid = 'public.tasks_project_idx'::regclass;DROP INDEX public.tasks_project_idx;indisvalid가 true여야 한다. 실제 배포에서는 마이그레이션 도구가 모든 명령을 자동으로 한
트랜잭션에 넣는지도 확인한다.
되돌리기의 의미
섹션 제목: “되돌리기의 의미”새 컬럼을 추가했다가 지우는 것과 기존 컬럼을 지운 뒤 되살리는 것은 다르다. 스키마를 되돌리는 SQL이 이미 삭제한 데이터까지 복원하지는 않는다. 큰 데이터 채우기는 작은 묶음으로 진행하고, 진행 위치와 재실행 시 중복 처리 여부를 관리한다.
이해 확인: DB migration이 성공했는데 배포를 실패로 판단할 수 있을까? 그렇다. 구버전 앱 오류, 긴 잠금 대기, 새 필드 누락이 관찰되면 SQL 성공만으로 완료가 아니다.