icon

안동민 개발노트

12장 : 복구와 로깅

WAL과 로그 기반 복구

DBMS는 언제든 장애가 발생할 수 있습니다. 정전, 디스크 오류, 프로세스 충돌 등의 상황에서도 커밋된 트랜잭션의 데이터는 반드시 보존되어야 하고, 커밋되지 않은 트랜잭션의 데이터는 깔끔하게 제거되어야 합니다. 이 요구를 충족시키는 핵심 기술이 WAL(Write-Ahead Logging)입니다. 데이터를 변경하기 전에 반드시 로그를 먼저 디스크에 기록하라는 단순하면서도 강력한 원칙이 현대 데이터베이스 복구 메커니즘의 토대입니다.

WAL 없이 데이터베이스를 운영한다면, 장애 발생 시 이 데이터가 커밋된 건가, 아닌 건가?를 알 수 없게 됩니다. COMMIT을 실행했지만 데이터가 디스크에 반영되기 전에 전원이 꺼지면, 그 트랜잭션의 변경은 영원히 사라집니다. WAL은 이 문제를 해결합니다.


WAL 원칙

WAL의 핵심은 두 가지 규칙입니다.

WAL 처리 순서

트랜잭션이 데이터를 변경할 때의 내부 처리 순서를 따라가 봅시다.

이 순서에서 핵심은 ③번입니다. 데이터가 아직 디스크에 기록되지 않았더라도, 로그가 디스크에 있으면 장애 후 Redo로 데이터를 복원할 수 있습니다. 반대로 ④번에서 데이터가 디스크에 기록되지 않은 채 장애가 발생해도 문제없습니다.

WAL의 성능 이점

왜 데이터를 바로 디스크에 안 쓸까요? 로그는 순차 쓰기(Sequential Write)이고, 데이터는 랜덤 쓰기(Random Write)이기 때문입니다.

예를 들어 10건의 UPDATE가 서로 다른 테이블의 서로 다른 페이지에 흩어져 있다면, 데이터를 직접 쓰려면 10번의 랜덤 I/O가 필요합니다. 하지만 WAL을 쓰면 로그 파일에 10건의 레코드를 순차적으로 쓰는 것으로 충분합니다. 실제 데이터 페이지의 디스크 기록은 나중에 모아서 처리합니다.

No-Force / Steal 정책

WAL과 함께 사용되는 버퍼 관리 정책을 이해하면 복구 메커니즘의 전체 그림이 보입니다.

정책설명필요한 복구 동작
No-Force커밋돼도 데이터가 디스크에 없을 수 있음Redo 필요
Steal커밋 안 된 데이터가 디스크에 있을 수 있음Undo 필요

로그 레코드 구조

Redo/Undo 로그에는 각 변경에 대한 레코드가 기록됩니다. DBMS마다 구체적인 구조는 다르지만, 공통적으로 포함하는 정보가 있습니다.

각 필드의 역할을 설명합니다.

LSN (Log Sequence Number): 로그 레코드의 고유 식별자입니다. 단조 증가하는 값으로, 이벤트의 발생 순서를 나타냅니다. 데이터 페이지에도 "이 페이지에 마지막으로 적용된 로그의 LSN"이 기록되어 있어, 복구 시 어떤 로그까지 적용되었는지 판단할 수 있습니다.

TxID: 트랜잭션 식별자입니다. 같은 트랜잭션의 로그 레코드를 묶어서 추적할 수 있습니다.

이전 값 (Before Image): 변경 전의 값입니다. Undo(롤백) 시 이 값으로 되돌립니다.

이후 값 (After Image): 변경 후의 값입니다. Redo(재적용) 시 이 값으로 설정합니다.

PrevLSN: 같은 트랜잭션의 이전 로그 레코드를 가리킵니다. Undo 시 역방향으로 추적하는 연결 리스트 역할을 합니다.

물리적 로깅 vs 논리적 로깅


ARIES 복구 알고리즘

ARIES(Algorithm for Recovery and Isolation Exploiting Semantics)는 IBM에서 개발한 복구 알고리즘으로, 현대 대부분의 DBMS가 채택하고 있는 표준적인 복구 방법입니다.

ARIES의 세 가지 핵심 원칙은 WAL(Write-Ahead Logging), Repeating History During Redo, Logging Changes During Undo입니다. 이 원칙에 따라 세 단계로 진행됩니다.

ARIES 3대 원칙
1. WAL: 데이터 변경 전에 반드시 로그를 먼저 기록
2. Repeating History: Redo 시 커밋 여부와 무관하게 모든 변경을 재적용
3. Logging Undo: Undo 작업 자체도 CLR로 기록하여 멱등성 보장

1단계: 분석 (Analysis Phase)

마지막 체크포인트부터 로그 끝까지 순방향으로 읽으며, 장애 시점에 어떤 트랜잭션이 활동 중이었는지와 어떤 데이터 페이지가 Dirty(메모리에서 변경되었지만 디스크에 반영되지 않음)였는지를 파악합니다.

2단계: Redo (Redo Phase)

분석 단계에서 결정된 시작점부터 로그 끝까지 순방향으로 읽으며, 모든 변경을 재적용합니다. 커밋 여부와 관계없이 모든 로그 레코드를 재적용합니다. 이를 Repeating History라 합니다.

Redo 시에는 각 데이터 페이지의 pageLSN(이 페이지에 마지막으로 적용된 로그의 LSN)을 확인합니다. 로그 레코드의 LSN이 pageLSN보다 크면 아직 적용 안 된 변경이므로 Redo를 수행합니다. 이미 적용된 변경은 건너뜁니다(멱등성).

3단계: Undo (Undo Phase)

분석 단계에서 파악된 미완료 트랜잭션(ATT에 있는 트랜잭션)의 변경을 역방향으로 되돌립니다.

CLR(Compensation Log Record)은 Undo 작업 자체를 기록하는 로그입니다. Undo 도중에도 장애가 발생할 수 있으므로, Undo를 얼마나 진행했는지 기록해두어야 합니다. CLR 덕분에 Undo는 반복 수행해도 동일한 결과를 보장합니다(멱등성).

ARIES 복구 전체 흐름


체크포인트 (Checkpoint)

체크포인트는 메모리의 변경된 데이터(Dirty Pages)를 디스크에 기록하는 시점입니다. 체크포인트의 주된 목적은 복구 시간을 단축하는 것입니다. 체크포인트가 수행되면 해당 시점 이전의 로그는 Redo에서 건너뛸 수 있으므로, 체크포인트 간격이 짧을수록 장애 후 복구 시간이 줄어듭니다. 그러나 체크포인트 자체도 디스크 I/O를 발생시키므로 너무 빈번하면 정상 운영 성능이 저하됩니다. 운영 환경에서는 이 트레이드오프를 적절히 조정해야 합니다.

체크포인트가 필요한 이유

체크포인트의 종류

Fuzzy Checkpoint에서는 체크포인트 시점에 모든 Dirty 페이지가 디스크에 기록되는 것이 아닙니다. 따라서 복구 시 체크포인트의 DPT를 참조하여 Redo의 시작점을 결정해야 합니다.

DBMS별 체크포인트 설정

PostgreSQL
파라미터:
  checkpoint_timeout = 5min      → 최소 5분마다 체크포인트
  max_wal_size = 1GB             → WAL 크기가 1GB 초과 시 체크포인트
  checkpoint_completion_target = 0.9  → 체크포인트 기간 분산

프로세스:
  Checkpointer: 체크포인트 수행
  Background Writer: Dirty 페이지를 점진적으로 기록

수동 체크포인트:
  CHECKPOINT;
MySQL InnoDB
파라미터:
  innodb_max_dirty_pages_pct = 75  → Dirty 페이지 비율 75% 초과 시
  innodb_io_capacity = 200         → 초당 I/O 작업 수
  innodb_adaptive_flushing = ON    → 적응적 flush

프로세스:
  Page Cleaner Thread: Dirty 페이지를 디스크에 기록

로그 버퍼와 디스크 기록 시점

로그는 먼저 메모리의 로그 버퍼에 기록되고, 특정 시점에 디스크에 기록(flush)됩니다.

로그 디스크 기록 시점 (Oracle LGWR)
1. 트랜잭션이 COMMIT할 때 (필수 — WAL 규칙 2)
2. 로그 버퍼가 1/3 이상 찼을 때
3. 3초마다 주기적으로
4. DBWR이 Dirty 페이지를 기록하기 전에 (WAL 규칙 1)

로그 기반 시점 복구 (PITR)

WAL 로그를 활용하면 특정 시점으로 데이터베이스를 복구할 수 있습니다. 이를 PITR(Point-In-Time Recovery)이라 합니다.

PostgreSQL PITR
-- recovery.conf (PostgreSQL 12 이전)
-- postgresql.conf (PostgreSQL 12+)
restore_command = 'cp /archive/%f %p'
recovery_target_time = '2024-03-15 13:55:00'
Oracle PITR (RMAN)
-- RMAN에서 시점 복구
RMAN> RUN {
    SET UNTIL TIME "TO_DATE('2024-03-15 13:55:00', 'YYYY-MM-DD HH24:MI:SS')";
    RESTORE DATABASE;
    RECOVER DATABASE;
}

PITR은 잘못된 DELETE나 DROP TABLE 같은 인적 오류에서 복구할 때 매우 유용합니다. 실수하기 직전 시점으로 되돌릴 수 있기 때문입니다.


로그 관련 장애 유형과 복구

트랜잭션 장애

개별 트랜잭션의 실패(제약 위반, 교착 상태 등)로 인한 롤백입니다. Undo 로그를 사용하여 해당 트랜잭션의 변경만 되돌립니다. 다른 트랜잭션에는 영향을 주지 않습니다. 트랜잭션 장애는 정상적인 운영 중에도 빈번하게 발생하며, DBMS가 자동으로 처리합니다. 개발자가 명시적으로 ROLLBACK 명령을 실행하는 경우도 트랜잭션 장애의 일종으로, 동일하게 Undo 로그를 사용하여 변경을 되돌립니다.

시스템 장애 (Instance Crash)

정전, DBMS 프로세스 충돌, OS 충돌 등으로 인스턴스가 비정상 종료된 경우입니다. 인스턴스 재시작 시 ARIES 3단계 복구가 자동으로 수행됩니다. 디스크의 데이터 파일은 손상되지 않았으므로 로그만으로 복구 가능합니다. 시스템 장애 후 복구에 소요되는 시간은 체크포인트 간격에 의해 결정됩니다. 체크포인트가 자주 수행될수록 Redo 범위가 줄어들어 복구가 빠릅니다.

미디어 장애 (Disk Failure)

디스크 물리적 손상으로 데이터 파일이 손실된 경우입니다. 전체 백업 + 아카이브 로그를 사용하여 PITR로 복구합니다. 이 경우에는 로그 파일이 별도의 디스크나 아카이브 스토리지에 보존되어 있어야 합니다. 미디어 장애는 데이터 파일 자체가 손상되므로 가장 심각한 장애 유형입니다. 디스크 RAID 구성과 정기적인 전체 백업이 미디어 장애 대비의 핵심입니다.


아카이브 로그 vs 온라인 로그

DBMS의 로그 파일은 관리 모드에 따라 복구 가능 범위가 크게 달라집니다.

운영 환경에서 적절한 모드를 선택하는 것은 DBA의 핵심 업무 중 하나입니다.

Oracle 아카이브 로그 모드 설정
-- 현재 모드 확인
SELECT LOG_MODE FROM V$DATABASE;

-- ARCHIVELOG 모드로 변경 (MOUNT 상태에서)
ALTER DATABASE ARCHIVELOG;

로그 설계 실무 고려사항


요약 정리

개념설명
WAL데이터 변경 전 로그 먼저 기록하는 원칙
Redo 로그변경 후 값 기록, 커밋된 트랜잭션 복원용
Undo 로그변경 전 값 기록, 미완료 트랜잭션 롤백용
LSN로그 레코드 고유 번호, 적용 여부 판단 기준
ARIES분석→Redo→Undo 3단계 표준 복구 알고리즘
체크포인트Dirty 페이지 디스크 기록, 복구 시간 단축
CLRUndo 작업 자체를 기록, Undo 멱등성 보장
PITR로그 기반 특정 시점 복구
No-Force/Steal대부분의 DBMS 버퍼 정책, Redo+Undo 필요 근거
ARCHIVELOG아카이브 로그 모드, PITR을 위한 필수 설정
로그 다중화로그 파일을 여러 디스크에 복사하여 안전성 확보

WAL은 단순한 기법이 아니라 데이터베이스의 내구성(Durability)을 보장하는 근본 원리입니다. COMMIT 응답을 받은 클라이언트는 이 변경은 영구적이다라고 신뢰할 수 있는데, 그 신뢰의 기반이 바로 WAL입니다. 로그가 디스크에 기록되었으므로 어떤 장애가 발생해도 Redo를 통해 변경을 복원할 수 있기 때문입니다. ARIES 알고리즘은 이 WAL 원칙 위에서 분석, Redo, Undo의 3단계를 체계적으로 수행하여, 시스템 장애 후에도 데이터베이스를 일관된 상태로 확실하게 복원합니다.

다음 절에서는 Oracle의 RMAN과 실무 백업 전략을 다루겠습니다.