운영 환경에서 실제로 통하는 데이터베이스 마이그레이션 전략
Source: Dev.to
실제 프로덕션에서 작동하는 데이터베이스 마이그레이션 전략
데이터베이스 마이그레이션은 간단해 보이지만 새벽 3시에 프로덕션 테이블을 45분 동안 잠궈버린 상황을 복구하려고 할 때 비로소 그 어려움을 깨닫게 됩니다. 수십억 건의 레코드를 가진 시스템에서 마이그레이션을 수행해 본 결과, 안전하게 진행하는 방법을 정리했습니다.
대부분의 마이그레이션 튜토리얼은 다음과 같이 제시합니다:
class AddIndexesToOrders X% error rate, auto-rollback)
- □ 트래픽이 적은 시간대에 마이그레이션 예약
- □ 대기 중인 담당 엔지니어에게 알림
- □ 데이터베이스 백업 확인 (시점 복구 테스트 수행)
- □ 적절한 락 타임아웃 설정
- □ 전체 테이블 스캔 여부를 확인하기 위해 쿼리 플랜 검토
컬럼 이름을 바꾸는 작업은 네 단계로 진행됩니다:
마이그레이션 1: 새 컬럼 추가 (더블 라이트 시작)
ALTER TABLE users ADD COLUMN display_name VARCHAR(100);
# 애플리케이션을 업데이트하여 두 컬럼에 모두 WRITE하도록 함
# User.where(name: 'John').update(display_name: 'John')
# 백그라운드에서 실행
마이그레이션 2: 백필
UPDATE users SET display_name = name WHERE display_name IS NULL;
-- 10,000건씩 배치 처리하고 0.1초 대기
마이그레이션 3: 기존 컬럼 읽기 중단
# display_name만 읽도록 코드를 배포
# 모든 것이 정상 동작하는지 검증
마이그레이션 4: 기존 컬럼 삭제
ALTER TABLE users DROP COLUMN name;
-- PostgreSQL에서는 트랜잭션 외부에서 실행해야 함
테이블 팽창 방지와 무잠금 재패킹
-- 확장 기능 설치
CREATE EXTENSION pg_repack;
# 락 없이 부피가 큰 테이블 재패킹
pg_repack -d mydb -t orders --no-indexes
# 특정 인덱스와 함께 재패킹
pg_repack -d mydb -t orders -i idx_orders_user_id
# 마이그레이션 중 활성 쿼리 모니터링
pg_activity -h localhost -U postgres
-- 직접 쿼리로 확인
SELECT pid, state, query, query_start, now() - query_start AS duration
FROM pg_stat_activity
WHERE state != 'idle'
ORDER BY duration DESC;
락 모드를 이해하면 놀라움을 방지할 수 있습니다
| 락 모드 | 차단되는 작업 |
|---|---|
| Access Share | DROP TABLE, TRUNCATE |
| Row Share | DELETE, UPDATE, SELECT FOR UPDATE |
| Row Exclusive | INSERT, UPDATE, DELETE |
| Share Update Exclusive | ANALYZE, CREATE INDEX CONCURRENTLY |
| Share | CREATE INDEX (차단) |
| Share Row Exclusive | ALTER TABLE |
| Exclusive | REFRESH MATERIALIZED VIEW CONCURRENTLY |
| Access Exclusive | DROP TABLE, TRUNCATE, 대부분의 ALTER TABLE |
프로덕션 테이블에서 마이그레이션이 100 ms 이상 걸린다면 잘못된 것입니다. 다시 돌아가서 더 작은 조각으로 나누세요.
목표는 언제나: 다운타임 제로, 데이터 손실 제로, 즉시 롤백 가능.
여러분이 겪은 마이그레이션 공포 이야기는 무엇인가요? 위험한 마이그레이션을 위해 가장 즐겨 쓰는 전략은 무엇인가요?