본문 바로가기

로그·체크포인트와 장애 회복

커밋과 디스크 기록, 장애의 시점을 구분하고, 쓰기 전 로그와 redo, undo의 추상 모델로 장애 뒤에 무엇을 되살려야 하는지 따라갑니다. steal과 force의 페이지 정책, 체크포인트와 그림자 페이지, InnoDB의 redo와 undo, binary log의 역할, 롤백과 백업 복원의 차이를 다룹니다.

트랜잭션이 성공했다는 응답을 받았다고, 바뀐 페이지가 모두 데이터 파일에 이미 기록된 것은 아닙니다. DBMS는 메모리의 변경과 영구 저장을 조율하면서, 장애 뒤에도 확정된 사실을 되살려야 합니다. 이 글은 회복의 원리를 설명하며, 실제 서버를 강제로 종료하는 실험은 요구하지 않습니다.

어떤 상태를 되살릴 것인가

활동 중인 트랜잭션은 읽고 바꾸는 일을 합니다. 마지막 작업까지 마친 부분 완료(partially committed)의 상태는 아직 영구적인 커밋이 보장된 것이 아니며, 커밋의 기록과 필요한 지속성의 처리가 끝나야 완료로 다룰 수 있습니다. 실패하면 변경을 철회하고, 필요하면 새 트랜잭션으로 다시 시도합니다.

프로그램의 오류로 트랜잭션 하나만 실패한 경우, 전원이 꺼져 메모리의 내용이 사라진 경우, 데이터를 담은 장치 자체가 손상된 경우는 회복에 쓸 재료가 다릅니다. 그래서 ROLLBACK과 서버가 로그로 하는 장애 회복, 백업의 복원과 그 뒤의 로그 적용을 같은 작업으로 다루지 않습니다.

쓰기 전 로그와 redo, undo

쓰기 전 로그(Write-Ahead Logging, WAL)의 핵심은, 바뀐 데이터 페이지를 영구 저장하기 전에 그 변경을 회복할 로그를 먼저 영구 저장하는 순서입니다. 버퍼의 데이터를 고치기 전에 반드시 매번 디스크에 로그를 쓴다는 단순한 순서를 뜻하지는 않습니다.

redo는 필요한 변경을 다시 적용하고, undo는 확정되지 않은 변경을 되돌립니다. 다음 표는 설명을 위해 값만 적은 로그이며, InnoDB 로그 파일의 실제 형식이 아닙니다.

순서사건논리적인 잔액장애 때 확인할 기록
0처음 잔액10기존의 저장 상태
1T1: 10→77변경의 로그
2T1 커밋7커밋과 지속성의 기록
3T2: 7→55끝나지 않은 변경의 로그
4T2가 커밋하기 전에 장애회복할 대상T1은 확정, T2는 미확정

이 단순한 모델에서 최종적으로 확정된 값은 7입니다. 데이터 페이지에 예전 값 10이 남아 있다면 확정된 변경을 다시 적용할 기록이 필요하고, 끝나지 않은 5가 기록될 수 있는 설계라면 그것을 철회할 기록도 필요합니다. 실제 회복에서 redo의 범위는 "커밋한 트랜잭션만 다시 실행한다"는 한 문장보다 복잡합니다.

교과서 회복 모델의 전제

지연 갱신(deferred update)의 단순한 모델은 커밋하지 않은 데이터를 데이터 파일로 내리지 않으므로 주로 redo를 설명하고, 즉시 갱신(immediate update)은 커밋 전의 페이지가 저장될 수 있어 undo가 필요할 수 있습니다. 이 차이를 판단하려면 두 가지 페이지 정책부터 정의해야 합니다.

steal은 아직 커밋하지 않은 트랜잭션이 바꾼 페이지도 데이터 파일에 저장할 수 있게 하는 정책입니다. 메모리를 비우는 데 도움이 되지만, 장애가 나면 미확정의 값이 디스크에 남을 수 있습니다. force는 커밋을 끝내기 전에 그 트랜잭션이 바꾼 데이터 페이지를 모두 영구 저장하는 정책이며, no-force는 커밋할 때 데이터 페이지의 저장을 강제하지 않으므로 확정의 로그가 있어도 데이터 파일에는 예전 값이 남을 수 있습니다.

다음 표는 데이터 페이지의 값만 생각한 단순한 회복 모형입니다. 필요한 로그와 저장의 순서가 지켜지고 페이지 쓰기의 손상 같은 다른 문제는 없다는 전제이며, 실제 데이터베이스의 로그 재생 범위나 메타데이터의 회복 전체를 판정하는 표는 아닙니다.

페이지 정책미확정 값이 데이터 파일에 남을 수 있나확정 값이 아직 데이터 파일에 없을 수 있나이 모형에서 필요한 회복
no-steal + force아니요아니요이 두 이유로는 undo도 redo도 필요 없다
steal + force예아니요미확정 값의 undo
no-steal + no-force아니요예확정 값의 redo
steal + no-force예예undo와 redo 모두

앞의 잔액 예제에서 목표 값 7을 두 장애 상태에 적용해 봅니다. 첫째, T1의 10→7 로그와 커밋의 기록은 영구 저장되었지만 no-force여서 데이터 페이지는 아직 10이라면, 확정된 로그를 따라 10→7을 다시 반영하는 redo가 필요합니다. 둘째, T1 뒤에 T2의 7→5 로그가 저장되고 steal로 미확정 페이지의 5까지 내려간 뒤 장애가 났다면, T2는 커밋하지 않았으므로 로그의 예전 값 7로 되돌리는 undo가 필요합니다.

두 상태의 데이터 파일은 각각 10과 5로 다르지만, 되살릴 확정된 사실은 같은 7입니다. 이 순차적인 단일 값의 추적을 모든 동시 트랜잭션의 회복 알고리즘으로 일반화하지는 않습니다. WAL은 로그를 먼저 저장하는 순서이고 steal과 force는 데이터 페이지를 언제 저장할지의 정책이므로, 서로 다른 질문입니다. 회복 정책과 WAL에 관한 강의 노트는 페이지 정책에서 생기는 원자성과 지속성의 문제를 로그의 규칙과 이어 설명합니다.

체크포인트(checkpoint)는 회복을 시작할 로그의 위치와 관련된 상태를 관리해, 재시작할 때의 작업을 줄입니다. 그러나 체크포인트가 모든 트랜잭션이 끝났다는 뜻이거나, 그 앞의 undo가 모두 필요 없어졌다는 뜻은 아닙니다. 그림자 페이지(shadow paging)는 예전 페이지와 새 페이지를 나누어 두고 완료할 때 참조를 바꾸는 대안이며, 페이지와 참조를 쓰는 순서와 원자성의 조건이 필요합니다. InnoDB가 이 방식으로 구현되었다고 설명하지는 않습니다.

장애 시점의 값과 확정 값을 구분한다

첫 변경이 커밋된다. 확정된 변경만 반영한다. 각 단계의 값과 전환 이유를 비교합니다.

장애 시점의 값과 확정 값을 구분한다

초기 10에서 T1이 7로 커밋하고 T2가 5로 바꿨지만 미커밋 상태에서 장애가 발생한 추상 모델입니다.

장애 시점의 값과 확정 값을 구분한다초기 10에서 T1이 7로 커밋하고 T2가 5로 바꿨지만 미커밋 상태에서 장애가 발생한 추상 모델입니다. T1 완료:10 → 7 커밋;T2 미완료:7 → 5 후 장애;확정 상태 복원:목표 값: 7。화살표는 설명용 사건 순서이며 실제 시간 간격이나 InnoDB 로그 형식이 아닙니다. 페이지 정책에 따라 redo/undo의 적용 근거를 확인합니다.T1 완료10 → 7 커밋T2 미완료7 → 5 후 장애확정 상태 복원목표 값: 7
장애 시점의 값과 확정 값을 구분한다초기 10에서 T1이 7로 커밋하고 T2가 5로 바꿨지만 미커밋 상태에서 장애가 발생한 추상 모델입니다. T1 완료:10 → 7 커밋;T2 미완료:7 → 5 후 장애;확정 상태 복원:목표 값: 7。화살표는 설명용 사건 순서이며 실제 시간 간격이나 InnoDB 로그 형식이 아닙니다. 페이지 정책에 따라 redo/undo의 적용 근거를 확인합니다.T1 완료10 → 7 커밋T2 미완료7 → 5 후 장애확정 상태 복원목표 값: 7
화살표는 설명용 사건 순서이며 실제 시간 간격이나 InnoDB 로그 형식이 아닙니다. 페이지 정책에 따라 redo/undo의 적용 근거를 확인합니다.

이 예의 조건

  • 본문과 전체 도표의 예를 따라 T1 완료부터 복구 목표까지 진행합니다.
관계도의 보충 설명
전체 도표의 표기·관계·조건
구분전체 도표의 내용
관계 설명초기 10에서 T1이 7로 커밋하고 T2가 5로 바꿨지만 미커밋 상태에서 장애가 발생한 추상 모델입니다. T1 완료: 10 → 7 커밋; T2 미완료: 7 → 5 후 장애; 확정 상태 복원: 목표 값: 7. 화살표는 설명용 사건 순서이며 실제 시간 간격이나 InnoDB 로그 형식이 아닙니다. 페이지 정책에 따라 redo/undo의 적용 근거를 확인합니다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
T1 완료

첫 변경이 커밋된다.

  • 확정·미확정 값 1: 10→7
  • 확정·미확정 값 2: T1 committed
  • 확정 값 7
T2 중 장애

다음 변경은 아직 확정되지 않았다.

  • 확정·미확정 값 1: 7→5
  • 확정·미확정 값 2: T2 uncommitted
  • 장애 발생
복구 목표

확정된 변경만 반영한다.

  • 확정·미확정 값 1: 목표 7
  • 확정·미확정 값 2: T2 효과 제거
  • 페이지 정책과 로그의 redo/undo 근거는 별도

장애 시점의 값과 확정 값을 구분한다

첫 변경이 커밋된다. 확정된 변경만 반영한다. 각 단계의 값과 전환 이유를 비교합니다.

장애 시점의 값과 확정 값을 구분한다

초기 10에서 T1이 7로 커밋하고 T2가 5로 바꿨지만 미커밋 상태에서 장애가 발생한 추상 모델입니다.

장애 시점의 값과 확정 값을 구분한다초기 10에서 T1이 7로 커밋하고 T2가 5로 바꿨지만 미커밋 상태에서 장애가 발생한 추상 모델입니다. T1 완료:10 → 7 커밋;T2 미완료:7 → 5 후 장애;확정 상태 복원:목표 값: 7。화살표는 설명용 사건 순서이며 실제 시간 간격이나 InnoDB 로그 형식이 아닙니다. 페이지 정책에 따라 redo/undo의 적용 근거를 확인합니다.T1 완료10 → 7 커밋T2 미완료7 → 5 후 장애확정 상태 복원목표 값: 7
장애 시점의 값과 확정 값을 구분한다초기 10에서 T1이 7로 커밋하고 T2가 5로 바꿨지만 미커밋 상태에서 장애가 발생한 추상 모델입니다. T1 완료:10 → 7 커밋;T2 미완료:7 → 5 후 장애;확정 상태 복원:목표 값: 7。화살표는 설명용 사건 순서이며 실제 시간 간격이나 InnoDB 로그 형식이 아닙니다. 페이지 정책에 따라 redo/undo의 적용 근거를 확인합니다.T1 완료10 → 7 커밋T2 미완료7 → 5 후 장애확정 상태 복원목표 값: 7
화살표는 설명용 사건 순서이며 실제 시간 간격이나 InnoDB 로그 형식이 아닙니다. 페이지 정책에 따라 redo/undo의 적용 근거를 확인합니다.

이 예의 조건

  • 본문과 전체 도표의 예를 따라 T1 완료부터 복구 목표까지 진행합니다.

관계도의 보충 설명

전체 도표의 표기·관계·조건
구분전체 도표의 내용
관계 설명초기 10에서 T1이 7로 커밋하고 T2가 5로 바꿨지만 미커밋 상태에서 장애가 발생한 추상 모델입니다. T1 완료: 10 → 7 커밋; T2 미완료: 7 → 5 후 장애; 확정 상태 복원: 목표 값: 7. 화살표는 설명용 사건 순서이며 실제 시간 간격이나 InnoDB 로그 형식이 아닙니다. 페이지 정책에 따라 redo/undo의 적용 근거를 확인합니다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
T1 완료

첫 변경이 커밋된다.

  • 확정·미확정 값 1: 10→7
  • 확정·미확정 값 2: T1 committed
  • 확정 값 7
T2 중 장애

다음 변경은 아직 확정되지 않았다.

  • 확정·미확정 값 1: 7→5
  • 확정·미확정 값 2: T2 uncommitted
  • 장애 발생
복구 목표

확정된 변경만 반영한다.

  • 확정·미확정 값 1: 목표 7
  • 확정·미확정 값 2: T2 효과 제거
  • 페이지 정책과 로그의 redo/undo 근거는 별도

InnoDB에서 이어 보기

InnoDB는 redo로 변경을 회복하고, 끝나지 않은 트랜잭션은 롤백합니다. undo는 롤백뿐 아니라 8장 3편의 MVCC가 과거의 행 버전을 읽는 데에도 쓰입니다. redo와 undo, binary log는 이름만 다른 한 로그가 아니며, binary log는 복제와 특정 시점으로의 복구에 쓰는 서버 계층의 기록입니다(InnoDB 회복, 다중 버전과 undo).

다음 실습은 지속성에 관한 두 설정을 확인하고, 한 연결에서 ROLLBACK과 COMMIT의 결과를 봅니다.

설정 확인과 트랜잭션 철회 관찰
CREATE DATABASE IF NOT EXISTS cs_db_lab;
USE cs_db_lab;
SELECT @@innodb_flush_log_at_trx_commit AS flush_at_commit,
       @@sync_binlog AS sync_binlog;
DROP TABLE IF EXISTS cs_recovery_account;
CREATE TABLE cs_recovery_account(id INT PRIMARY KEY,balance INT NOT NULL) ENGINE=InnoDB;
INSERT INTO cs_recovery_account VALUES(1,100);
START TRANSACTION;
UPDATE cs_recovery_account SET balance=70 WHERE id=1;
SELECT balance FROM cs_recovery_account WHERE id=1;
ROLLBACK;
SELECT balance FROM cs_recovery_account WHERE id=1;
START TRANSACTION;
UPDATE cs_recovery_account SET balance=90 WHERE id=1;
COMMIT;
SELECT balance FROM cs_recovery_account WHERE id=1;
flush_at_commit | sync_binlog
1               | 1

balance
70

balance
100

balance
90

두 설정이 1이면 기본값으로, 커밋마다 redo 로그를 디스크에 쓰고 binary log도 동기화한다는 뜻입니다. 같은 연결의 잔액은 트랜잭션 안의 70, ROLLBACK 뒤의 100, COMMIT 뒤의 90으로 나옵니다. 이 실습으로 확인한 것은 자기 변경과 ROLLBACK, COMMIT의 결과일 뿐, 장애 뒤의 재시작이나 저장 장치의 지속성, 백업의 복원을 확인한 것은 아닙니다. 설정값은 기록하되, 실습을 이유로 운영 서버의 지속성 설정을 낮추지 않습니다.

더 깊이: 커밋의 지속성을 떠받치는 InnoDB의 장치

innodb_flush_log_at_trx_commit의 기본값 1은 커밋할 때마다 로그를 쓰고 디스크로 플러시하는 설정이며, MySQL 매뉴얼은 완전한 ACID를 지키려면 이 값이 필요하다고 적습니다. 0이면 로그를 1초에 한 번 쓰고 플러시하며, 2이면 커밋마다 로그를 쓰되 플러시는 1초에 한 번 합니다. 두 설정에서는 아직 플러시되지 않은 트랜잭션을 장애로 잃을 수 있고, 1초라는 간격도 보장되지 않아 잃는 범위가 달라질 수 있습니다. 교과서 모델의 "커밋하기 전에 로그를 안정된 저장소에 쓴다"는 규칙을 그대로 지키는 것은 1뿐인 셈입니다.

redo 로그 파일이 차지할 디스크 공간은 innodb_redo_log_capacity가 정합니다. 파일은 데이터 디렉터리의 #innodb_redo 안에 있고, InnoDB는 이 공간을 32개의 파일로 나누어 유지하려 합니다. 로그가 쌓인 위치는 계속 커지는 LSN(log sequence number)으로 나타내며, 체크포인트를 할 때 InnoDB는 체크포인트 LSN을 파일의 헤더에 저장하고 그보다 오래된 로그를 잘라 냅니다. 재시작한 뒤의 회복은 가장 최근의 체크포인트 LSN에서 시작하므로, 앞에서 본 체크포인트의 원리가 그대로 쓰입니다(InnoDB의 redo 로그).

로그만으로 해결하지 않는 문제도 있습니다. 페이지를 쓰는 도중에 운영체제나 저장 장치, mysqld가 멈추면 데이터 파일에 반쯤 쓰인 페이지가 남을 수 있습니다. 그래서 InnoDB는 버퍼 풀에서 내보내는 페이지를 제자리에 쓰기 전에 이중 쓰기 버퍼(doublewrite buffer)에 먼저 쓰고, 회복할 때 거기서 온전한 페이지 사본을 찾습니다. 데이터를 두 번 쓰지만 이중 쓰기 버퍼에는 큰 덩어리로 순차적으로 쓰고 fsync()도 한 번만 부르므로, I/O의 비용이 두 배가 되지는 않습니다. fsync()가 파일에 쓴 데이터를 저장 장치까지 내려보내고 끝날 때까지 기다리게 하는 과정은 운영체제의 파일 시스템 실무에서 다룹니다.

백업과 복구는 다르다

로그가 남아 있어도 같은 장치의 데이터와 로그를 함께 잃으면, 되살릴 재료가 모자랄 수 있습니다. 백업은 따로 복원할 수 있는 사본이고, 원하는 시점까지 되살리려면 알맞은 뒤의 로그와 일관된 기준 시점도 필요합니다. 사본 파일이 있다는 것보다, 별도의 환경에서 실제로 복원해 무결성까지 확인했다는 기록이 중요합니다. 오류까지 그대로 복제한 복제 서버도 과거의 정상 시점에 대한 백업을 대신하지 못합니다.

정리

  • 커밋이 성공했다는 것과 바뀐 데이터 페이지가 디스크에 기록되었다는 것은 다른 사실입니다.
  • WAL은 페이지보다 그 변경을 회복할 로그를 먼저 영구 저장하는 순서입니다.
  • steal은 미확정 값의 undo를, no-force는 확정 값의 redo를 필요하게 만듭니다.
  • 체크포인트는 회복을 시작할 위치를 줄일 뿐, 끝나지 않은 트랜잭션을 커밋시키지 않습니다.
  • ROLLBACK과 장애 회복, 백업의 복원은 쓰는 재료와 확인할 대상이 서로 다릅니다.

연습 문제

1. 확정된 잔액과 체크포인트

T1이 잔액을 10에서 8로 바꾸어 커밋했고, T2가 8에서 6으로 바꾼 뒤 커밋하지 않은 채 장애가 났습니다. 추상 모델에서 확정된 잔액은 얼마인가요? T2가 체크포인트 뒤에 시작했다는 이유만으로 T2의 철회를 생략해도 되나요?

해설 보기

확정된 잔액은 8입니다. T2의 6은 커밋되지 않았으므로 되살릴 대상이 아닙니다. 체크포인트는 끝나지 않은 트랜잭션을 커밋시키지 않으므로, T2가 언제 시작했든 그 변경이 데이터 파일에 내려갔을 수 있다면 회복 모델의 undo 조건을 확인해 철회해야 합니다.

2. no-steal과 no-force

no-steal과 no-force의 정책에서, T1의 로그와 커밋은 영구 저장되었지만 데이터 페이지는 아직 10입니다. 연습 문제 1의 상황이라면 어떤 방향의 회복이 필요한가요?

해설 보기

no-steal이므로 T2의 미확정 값 6은 데이터 파일에 내려가지 않아 undo할 것이 없습니다. 그러나 no-force이므로 확정된 8도 아직 데이터 파일에 반영되지 않았습니다. 그래서 이 값의 모형에서는 T1의 10→8을 redo해야 합니다.

3. 롤백 실습과 백업 복원

본문의 ROLLBACK 실습을 통과한 것을 "백업 복원 시험 통과"로 기록하면 왜 틀리나요?

해설 보기

ROLLBACK은 살아 있는 서버에서 트랜잭션 하나를 철회한 것이고, 백업의 복원은 별도의 사본으로 서버와 데이터를 되살리는 작업이어서 확인하는 대상이 다릅니다. 롤백이 된다고 해서 백업 파일이 실제로 복원되는지, 복원한 데이터가 일관된지, 원하는 시점까지 로그를 적용할 수 있는지는 알 수 없습니다. 백업은 별도의 환경에서 실제로 복원해 보고 무결성을 확인해야 시험한 것입니다.

회복은 커밋의 상태와 페이지의 정책, 로그의 순서를 잇는 문제입니다. 다음 글 계정·역할과 최소 권한에서는 누가 이런 데이터에 접근할 수 있는지를 계정과 역할로 제한합니다.