데이터베이스 학습 노트 목차

회복·WAL·백업 — 장애에서 데이터를 지킨다

DB는 "죽는 것"을 전제로 설계된다

은행 이체 한 건을 처리하는 도중 전원이 나간다. 디스크가 물리적으로 깨진다. 운영자가 실수로 DELETE FROM users에서 WHERE를 빠뜨린다. 이 셋은 언젠가 반드시 일어난다 — 그래서 DB는 "장애가 없는 척"이 아니라 "장애가 나도 데이터를 잃지 않게" 설계된다. [04 ACID]에서 약속한 *원자성(전부-또는-전무)·지속성(커밋된 건 살아남음)*은 말로만 되는 게 아니다 — 그것을 물리적으로 보장하는 메커니즘이 **WAL(Write-Ahead Logging)**과 **회복(recovery)**이고, 사람의 실수와 디스크 손상까지 막는 최후의 안전망백업이다. (출처: Silberschatz DBSC 7e Ch 19 Recovery / PostgreSQL — WAL.)

장애의 세 종류 — 막는 방법이 다르다

장애는 심각도와 대응이 다른 세 종류로 나뉜다.

  • 트랜잭션 실패 — 제약 위반·교착·사용자 취소. 그 트랜잭션 하나만 롤백하면 끝(가장 가벼움).
  • 시스템 크래시 — 전원·OS 다운. 메모리(버퍼)에 있던 내용은 소실되지만 디스크는 살아 있다 → 디스크의 로그로 복구한다.
  • 미디어 장애 — 디스크 자체가 손상. 로그도 데이터도 함께 날아가니 → 백업 + 보관된 로그로 복원해야 한다.

WAL과 회복은 앞의 둘을, 백업은 세 번째와 사람의 실수를 막는다. 이 분류를 잡고 가면 각 메커니즘이 왜 필요한지가 분명해진다.

WAL — 로그를 데이터보다 먼저 쓴다

크래시를 견디는 핵심 아이디어는 단 하나다 — 데이터 파일을 바꾸기 전에, 그 변경 내역을 로그에 먼저 디스크에 기록한다(Write-Ahead). 왜 이 순서가 결정적일까? 데이터를 바꾸는 일은 디스크 곳곳의 페이지를 랜덤하게 쓰는 비싼 작업이라 바로바로 디스크에 못 내린다(메모리 버퍼에 모아 뒀다 나중에 내린다). 그 사이 크래시가 나면 메모리 변경은 사라진다 — 그래서 커밋 시점에 "변경의 기록"만이라도 디스크에 안전하게 박아 두는 것이다.

다이어그램 로딩 중…

이 규칙의 두 이득 — 첫째 지속성: 커밋 순간 데이터 페이지는 아직 메모리에 있어도 로그만 디스크에 있으면, 크래시 후 로그를 다시 적용해 복원할 수 있으니 커밋된 것은 절대 사라지지 않는다. 둘째 성능: 느린 랜덤 데이터 쓰기를 뒤로 미루고, 빠른 순차 로그 쓰기만 커밋 경로에 두니 오히려 빨라진다.

  • PostgreSQLWAL(redo 로그). 롤백/MVCC용 undo는 힙의 옛 튜플로 처리한다([05]).
  • MySQL InnoDBredo 로그(지속성·롤포워드)와 undo 로그(롤백·MVCC)를 분리한다([09]).

redo와 undo — 롤포워드와 롤백

크래시 직후, DB는 디스크의 데이터 파일이 어중간한 상태임을 발견한다 — 커밋됐는데 아직 안 내려간 변경도 있고, 커밋도 안 됐는데 미리 내려간 변경도 있다. 복구는 이를 두 방향으로 바로잡는다.

  • redo(롤포워드)커밋은 됐지만 데이터 파일엔 아직 반영 안 된 변경을, 로그를 보고 다시 적용한다 → 지속성 보장(커밋된 건 살린다).
  • undo(롤백)커밋 안 된 채 데이터 파일에 미리 반영된 변경을, 로그를 보고 되돌린다원자성 보장(미완성은 없던 일로).

표준 알고리즘 ARIES분석(어디까지 했나) → redo(전부 다시) → undo(미완성 되돌리기) 순으로 크래시 직전의 일관된 상태를 정확히 복원한다. 예컨대 *"A 계좌에서 빼고 B에 더하는 중 전원이 나갔다"*면 — 커밋 전이었으면 undo로 둘 다 원복(돈이 증발하지 않음), 커밋 후였으면 redo로 둘 다 확정된다.

체크포인트 — 복구를 빠르게 하는 지점

만약 복구가 로그의 맨 처음부터 다 훑어야 한다면, 오래 운영한 DB는 복구에 몇 시간이 걸릴 수 있다. 그래서 **체크포인트(checkpoint)**가 있다 — 주기적으로 메모리의 더티 페이지를 디스크에 내리고, "여기까진 데이터 파일에 반영됨"이라는 표시를 남긴다. 복구는 마지막 체크포인트 이후의 로그만 보면 되니 빨라진다. 다만 체크포인트가 잦으면 복구는 빠르지만 평상시 디스크 I/O 부담이 커지고, 드물면 반대다 — 운영 튜닝의 한 포인트다.

백업 — 논리 vs 물리, 전체 vs 증분

WAL은 살아 있는 디스크를 전제로 한다. 디스크가 통째로 깨지거나 사람이 실수로 데이터를 지우면 WAL로는 못 막는다 — 그래서 백업이 최후의 안전망이다.

다이어그램 로딩 중…
# 논리 백업 — SQL 텍스트로 덤프 (이식성 좋음, 일부 테이블만도 가능, 대용량엔 느림)
pg_dump mydb > backup.sql
mysqldump mydb > backup.sql

# 물리 백업 — 데이터 파일을 그대로 (빠름, 같은 버전/플랫폼에서 복원)
pg_basebackup -D /backup/base
  • 논리 백업SQL/데이터로 덤프하니 버전·플랫폼을 넘나드는 이식성이 좋고 일부만 복원도 되지만, 대용량에선 느리고 크다.
  • 물리 백업데이터 파일을 통째 복사하니 빠르고 대용량에 유리하지만 같은 버전·플랫폼에서만 복원된다.
  • 전체 vs 증분 — 매번 전체를 뜨면 무겁다. 증분 백업(PostgreSQL 17의 pg_basebackup --incremental)으로 지난 백업 이후 바뀐 부분만 떠 시간·용량을 아낀다.

PITR — 특정 시점으로 되돌리기

운영 DB의 필수 안전망이 **시점 복구(Point-In-Time Recovery)**다 — 기준 백업 + 그 이후의 WAL 아카이브를 합쳐 원하는 과거 시점으로 복원한다. 운영자가 오후 3시 5분에 실수로 테이블을 날렸다면오후 3시 4분까지의 상태로 되감을 수 있다(WHERE 빠진 DELETE의 구원).

기준 백업(base) + 연속 WAL 아카이브 → "3시 4분"까지만 redo를 적용해 복원

복제 ≠ 백업 — 가장 흔한 오해

*"복제본이 있으니 백업은 필요 없다"*는 위험한 착각이다. 복제([12])는 고가용성(한 대가 죽어도 다른 대가 서비스), 백업은 과거 복원(실수·손상 대비)으로 목적이 다르다. 결정적으로 — 실수로 DELETE한 게 복제본에도 즉시 전파되어 모든 복제본에서 함께 사라진다. 복제는 DELETE 실수를 막아 주지 못한다. 그래서 복제와 백업은 둘 다 필요하다.

RPO·RTO — 얼마를 잃고, 얼마 만에 복구하나

백업 전략은 두 숫자로 정한다. **RPO(Recovery Point Objective)**는 허용 가능한 데이터 손실량(예: "최대 5분치까진 잃어도 됨") — 백업·WAL 아카이브 주기로 정해진다. **RTO(Recovery Time Objective)**는 허용 가능한 복구 시간(예: "30분 안에 다시 서비스") — 백업 방식·자동화로 정해진다. 그리고 가장 중요한 한 가지복구를 정기적으로 실제로 테스트해야 한다. 복원해 본 적 없는 백업은 백업이 아니다(막상 복원하려니 깨져 있는 경우가 흔하다).

정리 — 로그로 살리고, 백업으로 대비한다

회복의 핵심 — WAL(로그를 데이터보다 먼저 디스크에)로 크래시에도 원자성·지속성 보장(redo 롤포워드/undo 롤백), 체크포인트로 복구를 빠르게, 백업(논리/물리·전체/증분)과 PITR(WAL 아카이브)로 미디어 장애·사람 실수 대비, 복제는 백업이 아니다, RPO/RTO를 정하고 복구를 실제로 테스트. DB는 죽는 걸 전제로 살아남게 설계된다.

한 줄 요약 — WAL=로그를 데이터보다 먼저 디스크에(지속성+성능). 크래시 복구=redo(커밋된 것 다시 적용·롤포워드)+undo(미완성 되돌림·롤백)+체크포인트(복구 시작점). 백업: 논리(pg_dump·이식) vs 물리(pg_basebackup·빠름), 전체 vs 증분. PITR=기준 백업+WAL 아카이브로 임의 시점(DELETE 실수 구원). 복제≠백업(실수가 복제됨). RPO(손실량)/RTO(복구시간) 정하고 복구 테스트 필수.

(출처: Silberschatz DBSC 7e Ch 19 / PostgreSQL — WAL / Continuous Archiving & PITR / Backup / MySQL — InnoDB Recovery / Backup methods.)

동시성 제어 — 2PL·MVCC·락·데드락(PostgreSQL·MySQL)인덱스 — B+Tree·클러스터형·커버링(PostgreSQL·MySQL)