DB는 장애가 발생해도 어떻게 데이터를 복구할까? Redo와 Undo의 원리
애플리케이션에서 다음 트랜잭션이 정상적으로 커밋됐다고 가정해 보자.
BEGIN;
UPDATE account
SET balance = balance - 10000
WHERE account_id = 1;
COMMIT;
애플리케이션은 COMMIT 성공 응답을 받았다. 그런데 직후 DB 서버의 전원이 내려갔다.
이때 데이터 페이지가 실제 데이터 파일에 기록되기 전이었다면 어떻게 될까?
반대로 아직 커밋하지 않은 트랜잭션의 변경 내용이 데이터 파일에 먼저 기록된 상태에서 DB가 종료됐다면, 해당 변경은 어떻게 제거할까?
DBMS는 이 문제를 해결하기 위해 일반적으로 다음 두 종류의 복구 정보를 관리한다.
- Redo: 변경 내용을 다시 적용하기 위한 정보
- Undo: 변경 전 상태로 되돌리기 위한 정보
핵심을 먼저 정리하면 다음과 같다.
DB 장애 복구는 장애 직전의 상태를 Redo로 재현하고,
그중 커밋되지 않은 트랜잭션을 Undo로 제거하는 과정이다.
다만 실제 구현은 DBMS마다 다르다. Oracle과 MySQL InnoDB는 명시적인 Undo 구조를 사용하지만, PostgreSQL은 전통적인 의미의 Undo 로그 대신 MVCC 튜플 버전과 WAL을 중심으로 복구한다.
이 글에서는 특정 DBMS에 종속되지 않는 기본 원리를 먼저 살펴보고, 이후 MySQL InnoDB와 Oracle을 기준으로 실제 동작을 설명한다.
1. 데이터는 COMMIT과 동시에 데이터 파일에 기록되지 않는다
많은 개발자가 처음에는 다음과 같이 생각한다.
UPDATE 실행
→ 데이터 파일 수정
→ COMMIT
하지만 대부분의 상용 DBMS는 이 방식으로 동작하지 않는다.
디스크의 데이터 페이지를 트랜잭션마다 직접 수정하면 랜덤 I/O가 과도하게 발생하고, 트랜잭션 응답 시간이 디스크 쓰기 성능에 크게 의존하게 된다.
실제 동작은 대략 다음과 같다.
1. 디스크의 데이터 페이지를 메모리 버퍼로 읽는다.
2. 메모리상의 데이터 페이지를 변경한다.
3. 변경 내용을 로그 버퍼에 기록한다.
4. COMMIT 시 필요한 로그를 디스크에 기록한다.
5. 변경된 데이터 페이지는 나중에 데이터 파일에 기록한다.
메모리에서 변경됐지만 아직 데이터 파일에 기록되지 않은 페이지를 Dirty Page라고 한다.
예를 들어 계좌 잔액이 다음과 같이 변경됐다고 가정하자.
변경 전 잔액: 50,000원
변경 후 잔액: 40,000원
COMMIT 직후 DB 내부 상태는 다음과 같을 수 있다.
메모리 버퍼: 40,000원
데이터 파일: 50,000원
Redo 로그: 변경 기록 존재
즉, COMMIT이 성공했더라도 데이터 파일에는 아직 이전 값이 남아 있을 수 있다.
그럼에도 DB가 데이터의 영속성을 보장할 수 있는 이유가 바로 Redo 로그와 WAL이다.
2. Redo 로그란 무엇인가
Redo 로그는 데이터에 수행된 변경을 다시 적용할 수 있도록 기록한 복구 정보다.
개념적으로는 다음과 같은 정보를 가진다고 이해할 수 있다.
트랜잭션 ID: TX100
페이지 ID: PAGE-10
변경 위치: account.balance
변경 내용: 50,000 → 40,000
로그 순번: LSN 1520
실제 로그가 SQL 문장을 그대로 저장하는 것은 아니다.
DBMS에 따라 페이지의 특정 바이트 변경, 레코드 변경, 논리적 연산 등 서로 다른 형태로 기록될 수 있다. 중요한 점은 장애 발생 후 해당 변경을 재현할 수 있어야 한다는 것이다.
Oracle은 Redo 로그를 데이터베이스에 발생한 모든 변경을 기록하는 핵심 복구 구조로 설명하며, PostgreSQL 역시 WAL을 통해 데이터 파일에 반영되지 않은 변경을 장애 후 다시 적용한다.
3. WAL: 데이터 페이지보다 로그를 먼저 기록한다
Redo 기반 복구가 성립하려면 반드시 지켜야 하는 규칙이 있다.
변경된 데이터 페이지를 디스크에 기록하기 전에,
해당 변경을 복구할 수 있는 로그가 먼저 디스크에 기록돼야 한다.
이를 Write-Ahead Logging, 줄여서 WAL이라고 한다.
잘못된 순서
데이터 페이지 디스크 기록
→ 로그 디스크 기록
위 순서에서는 데이터 페이지 일부가 기록된 직후 장애가 발생하면, 해당 변경이 어떤 트랜잭션에서 발생했는지 또는 어떻게 복구해야 하는지 알 수 없게 된다.
따라서 다음 순서를 보장해야 한다.
정상적인 WAL 순서
Redo 로그 디스크 기록
→ 데이터 페이지 디스크 기록
COMMIT 시에도 일반적으로 데이터 페이지 전체를 즉시 기록하는 대신, 커밋에 필요한 로그를 안정적인 저장장치에 먼저 기록한다.
UPDATE 수행
↓
메모리의 데이터 페이지 변경
↓
Redo 로그 생성
↓
COMMIT 요청
↓
Redo 및 Commit 로그 Flush
↓
COMMIT 성공 응답
↓
Dirty Page는 이후 백그라운드에서 기록
PostgreSQL 공식 문서에서도 WAL의 핵심 원칙을 데이터 파일 변경보다 로그 기록이 먼저 영구 저장돼야 한다는 것으로 설명한다.
4. 왜 데이터 페이지가 아니라 로그를 먼저 저장할까
한 행의 컬럼 하나만 수정하더라도 DB는 일반적으로 페이지 단위로 데이터를 관리한다.
예를 들어 16KB 페이지에서 몇 바이트만 변경됐다고 가정하자.
데이터 파일을 즉시 저장한다면 16KB 페이지에 대한 랜덤 I/O가 발생할 수 있다.
반면 로그는 변경 정보를 로그 파일의 끝에 계속 추가하는 형태로 기록할 수 있다.
데이터 페이지 쓰기
- 페이지 단위
- 랜덤 I/O 발생 가능
- 여러 데이터 파일에 분산
- 트랜잭션마다 수행하면 비용이 큼
Redo 로그 쓰기
- 로그 파일 끝에 추가
- 순차 쓰기에 가까움
- 여러 트랜잭션의 로그를 묶어 기록 가능
- Group Commit 적용 가능
따라서 DB는 COMMIT마다 모든 데이터 페이지를 강제로 디스크에 기록하지 않고, 비교적 효율적인 로그 쓰기로 영속성을 먼저 확보한다.
이 구조는 다음 두 가지를 동시에 달성한다.
- COMMIT 응답 시간을 줄인다.
- 장애가 발생해도 커밋된 데이터를 복구한다.
5. Undo 로그란 무엇인가
Undo 로그는 데이터가 변경되기 전 상태를 복원할 수 있도록 유지하는 정보다.
잔액이 50,000원에서 40,000원으로 변경됐다면 개념적으로 다음과 같은 정보가 필요하다.
트랜잭션 ID: TX100
대상 레코드: account_id = 1
변경 전 balance: 50,000
트랜잭션이 롤백되면 Undo 정보를 이용해 변경 전 상태로 되돌린다.
현재 값: 40,000
Undo 값: 50,000
ROLLBACK
↓
50,000원으로 복원
Undo는 주로 다음 목적으로 사용된다.
- 명시적인
ROLLBACK - 장애 발생 후 미커밋 트랜잭션 제거
- MVCC 기반 일관성 읽기
Oracle 공식 문서는 Undo가 미커밋 트랜잭션의 롤백뿐 아니라, 다른 트랜잭션이 데이터를 변경하고 있을 때 과거 이미지를 제공하는 일관성 읽기에도 사용된다고 설명한다.
6. UPDATE가 실행될 때 Redo와 Undo는 어떻게 생성되는가
다음 SQL이 실행된다고 가정하자.
UPDATE account
SET balance = 40000
WHERE account_id = 1;
기존 잔액은 50,000원이다.
DB 내부 동작을 단순화하면 다음과 같다.
1. account 레코드가 포함된 페이지를 버퍼 캐시로 읽는다.
2. 변경 전 값 50,000원을 Undo 영역에 기록한다.
3. 메모리의 balance 값을 40,000원으로 변경한다.
4. Undo 변경과 데이터 페이지 변경에 대한 Redo 정보를 생성한다.
5. COMMIT 시 필요한 Redo 로그를 디스크에 Flush한다.
6. Dirty Page는 이후 적절한 시점에 데이터 파일로 기록한다.
여기서 주목해야 할 점은 Undo 자체도 DB가 관리하는 데이터라는 것이다.
예를 들어 Oracle에서는 Undo 세그먼트의 변경 역시 Redo 대상이 된다. 따라서 장애 복구 시 Redo를 적용하면 일반 데이터 변경뿐 아니라, 미커밋 트랜잭션을 되돌리는 데 필요한 Undo 정보도 함께 복구된다. Oracle 문서 역시 Roll Forward 과정에서 Undo 블록의 변경도 Redo로 복구된다고 설명한다.
7. 커밋된 변경이 데이터 파일에 반영되기 전에 장애가 발생한 경우
첫 번째 장애 상황을 살펴보자.
1. balance를 50,000원에서 40,000원으로 변경
2. Redo 로그를 디스크에 기록
3. COMMIT 완료
4. 데이터 페이지는 아직 데이터 파일에 기록되지 않음
5. DB 서버 장애 발생
장애 직전 상태는 다음과 같다.
Redo 로그: 50,000 → 40,000 변경 기록 존재
Commit 기록: 존재
데이터 파일: 여전히 50,000
메모리 버퍼: 장애로 유실
DB가 재시작되면 Redo 로그를 읽고 변경을 다시 적용한다.
데이터 파일: 50,000
↓ REDO
복구된 상태: 40,000
이를 Roll Forward 또는 Redo 복구라고 한다.
Redo를 통해 애플리케이션이 이미 성공 응답을 받은 커밋 결과를 보존한다.
이는 ACID 속성 중 Durability, 즉 영속성을 보장하는 핵심 메커니즘이다.
8. 커밋되지 않은 데이터가 데이터 파일에 기록될 수 있다
이번에는 반대 상황을 보자.
BEGIN;
UPDATE account
SET balance = 40000
WHERE account_id = 1;
-- COMMIT하지 않은 상태
많은 개발자가 다음과 같이 오해한다.
커밋하지 않은 데이터는 데이터 파일에 기록되지 않을 것이다.
하지만 일반적인 DBMS의 버퍼 관리 정책에서는 커밋되지 않은 변경을 포함한 Dirty Page도 데이터 파일에 기록될 수 있다.
그 이유는 다음과 같다.
- 버퍼 캐시 공간 확보
- 체크포인트 수행
- 백그라운드 Writer 동작
- 메모리 압박
- 페이지 Flush 정책
하나의 데이터 페이지에는 여러 트랜잭션이 수정한 레코드가 함께 존재할 수도 있다.
따라서 특정 트랜잭션이 커밋될 때까지 해당 페이지 전체를 디스크에 기록하지 않는 방식은 버퍼 관리와 I/O 효율 측면에서 제약이 크다.
이러한 정책을 일반적으로 Steal 정책이라고 한다.
커밋 전 상태
Undo: 변경 전 값 50,000 존재
메모리 페이지: 40,000
데이터 파일: 40,000까지 먼저 기록될 수 있음
Commit 기록: 없음
이 상태에서 DB가 종료되면 미커밋 데이터가 데이터 파일에 남아 있게 된다.
따라서 재시작 후 Undo가 필요하다.
데이터 파일: 40,000
↓ UNDO
최종 상태: 50,000
Undo는 미완료 트랜잭션의 결과를 제거하여 ACID 속성 중 Atomicity, 즉 원자성을 보장한다.
9. 장애 복구는 왜 Redo 후 Undo 순서로 수행할까
장애 복구를 단순하게 설명할 때 다음과 같이 말하는 경우가 많다.
커밋된 트랜잭션 → Redo
미커밋 트랜잭션 → Undo
개념을 처음 이해하는 데는 유용하지만, ARIES 계열 복구 알고리즘의 실제 동작을 정확히 표현한 설명은 아니다.
대표적인 ARIES 복구 알고리즘은 다음 순서로 진행된다.
Analysis
→ Redo
→ Undo
Analysis
장애 시점에 다음 정보를 재구성한다.
- 어떤 트랜잭션이 실행 중이었는가
- 어떤 트랜잭션이 완료됐는가
- 어떤 페이지가 Dirty Page였는가
- Redo를 어디서부터 시작해야 하는가
Redo
장애 직전 DB가 수행했던 변경 이력을 다시 재현한다.
여기에는 커밋된 트랜잭션뿐 아니라, 장애 시점에 아직 커밋하지 않은 트랜잭션의 변경도 포함될 수 있다.
Undo
Redo가 끝난 후, 장애 시점까지 커밋하지 못한 트랜잭션의 변경만 역순으로 제거한다.
ARIES 논문은 이러한 복구 방식을 “Repeating History”로 설명한다. 즉, 먼저 장애 직전 상태를 로그를 통해 재현한 다음, 완료되지 않은 트랜잭션을 롤백한다.
10. 미커밋 트랜잭션까지 Redo하는 이유
다음 상황을 가정해 보자.
TX1: 커밋 완료
TX2: 커밋되지 않음
TX2가 데이터를 변경하면서 Undo 레코드도 생성했다.
그런데 Undo 레코드가 포함된 페이지가 장애 전에 데이터 파일에 완전히 기록되지 않았을 수 있다.
TX2 데이터 변경: 일부 디스크 반영
TX2 Undo 정보: 디스크 미반영 가능
이 상태에서 TX2의 일반 데이터 변경만 무시하고 바로 Undo하려 하면, Undo에 필요한 정보가 디스크에 존재하지 않을 수 있다.
따라서 복구 과정에서는 먼저 로그를 재생해 장애 직전 상태를 재현한다.
1. 데이터 페이지 변경 Redo
2. Undo 페이지 변경 Redo
3. 장애 직전 상태 재현
4. 미커밋 TX2 식별
5. 복구된 Undo 정보를 이용해 TX2 롤백
즉, Redo의 목적은 단순히 커밋된 데이터만 살리는 것이 아니다.
Redo는 DB를 장애 직전의 일관된 물리 상태로 재현하고,
Undo가 정상적으로 수행될 수 있는 기반까지 복구한다.
Oracle 역시 인스턴스 복구 과정에서 먼저 Redo를 적용해 커밋 및 미커밋 변경을 재현한 뒤, 미커밋 트랜잭션을 Undo하는 방식으로 설명한다.
11. 실제 장애 복구 시나리오
두 개의 트랜잭션이 있다고 가정하자.
-- TX100
BEGIN;
UPDATE account
SET balance = 40000
WHERE account_id = 1;
COMMIT;
-- TX200
BEGIN;
UPDATE account
SET balance = 20000
WHERE account_id = 2;
-- COMMIT 전에 장애 발생
장애 시점의 상태는 다음과 같을 수 있다.
| 구분 | TX100 | TX200 |
|---|---|---|
| Redo 기록 | 존재 | 존재 |
| Undo 기록 | 존재 | 존재 |
| Commit 기록 | 존재 | 없음 |
| 데이터 파일 반영 | 일부 또는 미반영 | 일부 반영 가능 |
| 최종 처리 | 유지 | 롤백 |
복구 과정은 다음과 같다.
1. Analysis
- TX100은 완료된 트랜잭션으로 식별
- TX200은 미완료 트랜잭션으로 식별
- 복구가 필요한 Dirty Page 식별
2. Redo
- 로그 기준으로 필요한 페이지 변경 재적용
- TX100과 TX200의 장애 직전 변경 이력 재현
3. Undo
- 미완료 트랜잭션 TX200의 변경 제거
- TX100의 변경은 유지
최종 결과는 다음과 같다.
TX100: 커밋 결과 보존
TX200: 변경 전 상태로 복원
12. Redo를 다시 적용하면 데이터가 중복 변경되지 않을까
예를 들어 다음 변경을 Redo한다고 생각해 보자.
balance = balance - 10000
이 연산을 두 번 수행하면 잔액이 중복 차감될 수 있다.
그러나 실제 물리적 Redo는 애플리케이션의 SQL 문장을 무조건 다시 실행하는 방식이 아니다.
DB는 일반적으로 LSN, Log Sequence Number와 페이지에 기록된 로그 순번을 비교한다.
Redo Record LSN: 1520
Page LSN: 1520
페이지가 이미 해당 변경까지 반영한 상태라면 Redo를 다시 수행하지 않는다.
Page LSN >= Redo LSN
→ 이미 반영된 로그
→ Redo 생략
반대로 페이지의 LSN이 더 낮다면 아직 반영되지 않은 변경으로 판단한다.
Page LSN < Redo LSN
→ 데이터 페이지에 미반영
→ Redo 수행
따라서 복구 과정은 모든 로그를 무조건 중복 실행하는 것이 아니라, 데이터 페이지와 로그의 상태를 비교해 필요한 변경만 적용한다.
Oracle도 SCN을 이용해 데이터 파일에 이미 반영된 변경과 추가 복구가 필요한 지점을 판단한다.
13. 체크포인트는 복구 범위를 줄인다
DB가 시작될 때 로그를 처음부터 끝까지 모두 읽어야 한다면, 운영 기간이 길수록 복구 시간이 계속 증가할 것이다.
이를 방지하기 위해 DB는 주기적으로 Checkpoint를 생성한다.
체크포인트는 단순히 “메모리의 모든 데이터를 디스크에 저장하는 순간”만을 의미하지 않는다. DBMS마다 구체적인 구현은 다르지만, 복구 관점에서는 다음 정보를 제공한다.
어느 로그 지점부터 복구를 시작해야 하는가
어떤 Dirty Page가 남아 있는가
어떤 트랜잭션이 진행 중이었는가
전체 로그가 다음과 같다고 가정하자.
LSN 1 -------------------------------------- LSN 100000
최근 체크포인트와 Redo 시작 지점이 LSN 95000이라면 장애 복구 시 전체 로그가 아니라 해당 지점 근처부터 확인할 수 있다.
전체 로그
|--------------------------------------------------|
복구 대상
|------------|
PostgreSQL은 장애 복구 시 최신 체크포인트 레코드에서 Redo 시작 지점을 찾고, 그 이전 변경은 이미 데이터 파일에 기록된 것으로 판단한다고 설명한다.
체크포인트 주기는 운영 성능과 장애 복구 시간 사이의 트레이드오프를 만든다.
체크포인트가 너무 잦은 경우
- 데이터 페이지 쓰기 증가
- 디스크 I/O 스파이크 가능
- 정상 운영 성능 저하 가능
체크포인트 간격이 너무 긴 경우
- 장애 시 재생할 로그 증가
- 복구 시간 증가
- 로그 보관량 증가
따라서 체크포인트는 단순 설정값이 아니라 RTO와 I/O 성능에 직접 영향을 주는 운영 요소다.
14. Redo와 Undo의 차이
| 구분 | Redo | Undo |
|---|---|---|
| 목적 | 변경 이력 재적용 | 변경 이전 상태 복원 |
| 주요 역할 | 장애 복구, 영속성 보장 | 롤백, 원자성, MVCC |
| 데이터 관점 | 변경 후 상태 또는 변경 연산 | 변경 전 상태 |
| 장애 복구 | 장애 직전 상태 재현 | 미커밋 변경 제거 |
| ACID | Durability | Atomicity |
| 대표 사용 | Crash Recovery | Rollback, Consistent Read |
| 적용 방향 | 로그 순방향 | 일반적으로 트랜잭션 변경의 역방향 |
15. Redo 로그와 Binary Log는 같은 것이 아니다
MySQL을 사용하는 환경에서는 Redo 로그와 Binary Log를 혼동하기 쉽다.
두 로그는 목적과 관리 주체가 다르다.
| 구분 | InnoDB Redo Log | MySQL Binary Log |
|---|---|---|
| 관리 계층 | InnoDB 스토리지 엔진 | MySQL 서버 계층 |
| 주요 목적 | Crash Recovery | 복제, CDC, PITR |
| 기록 관점 | 페이지·레코드 변경 중심 | Statement 또는 Row Event |
| 장애 복구 | InnoDB 데이터 복구 | 백업 이후 변경 재생 |
| 복제 사용 | 직접 사용하지 않음 | Replica 전파에 사용 |
Redo 로그는 DB 프로세스나 서버가 비정상 종료됐을 때 현재 데이터 파일을 일관된 상태로 복구하기 위한 로그다.
Binary Log는 다음과 같은 용도로 주로 사용된다.
- Source-Replica 복제
- CDC
- Point-in-Time Recovery
- 변경 이벤트 추적
따라서 다음 두 문장은 서로 다른 의미를 가진다.
Redo를 이용한 복구
→ 현재 InnoDB 데이터 파일의 Crash Recovery
Binary Log를 이용한 복구
→ 백업 시점 이후 논리 변경을 재생하는 시점 복구
16. Crash Recovery와 Backup Recovery도 다르다
Redo가 존재한다고 해서 디스크 자체가 손상된 상황까지 모든 데이터를 복구할 수 있는 것은 아니다.
장애 유형을 구분해야 한다.
Instance 또는 Process Crash
DB 프로세스 비정상 종료
OS 재부팅
서버 전원 장애
메모리 데이터 유실
데이터 파일과 Redo 로그가 정상적으로 남아 있다면 Crash Recovery를 수행할 수 있다.
Media Failure
디스크 손상
데이터 파일 삭제
스토리지 장애
파일 시스템 손상
이 경우에는 Redo만으로 원본 데이터 파일 전체를 복구할 수 없다.
일반적으로 다음이 필요하다.
Backup 복원
→ Archived Redo 또는 WAL Archive 적용
→ 필요한 시점까지 로그 재생
Oracle의 Archived Redo Log와 PostgreSQL의 WAL Archive는 이러한 미디어 복구 및 시점 복구에 활용된다. PostgreSQL은 연속된 WAL 아카이브를 이용해 백업 이후 변경을 재생하는 Point-in-Time Recovery를 지원한다.
17. MySQL InnoDB에서는 어떻게 동작할까
MySQL InnoDB에서는 다음 구성요소를 함께 이해해야 한다.
Buffer Pool
Redo Log
Undo Log
Doublewrite Buffer
Binary Log
Checkpoint
Redo Log
메모리의 Dirty Page가 데이터 파일에 기록되기 전에 장애가 발생해도 변경을 재적용할 수 있게 한다.
Undo Log
트랜잭션 롤백과 MVCC의 이전 버전 조회에 사용된다.
Doublewrite Buffer
페이지를 디스크에 기록하는 도중 장애가 발생해 페이지 일부만 저장되는 Torn Page 문제를 방지한다.
Binary Log
복제와 Point-in-Time Recovery에 사용된다.
즉, InnoDB 장애 복구를 단순히 Redo와 Undo만의 문제로 보면 부족하다.
Redo
→ 누락된 페이지 변경 복구
Undo
→ 미커밋 트랜잭션 롤백
Doublewrite
→ 손상되거나 부분 기록된 페이지 복구
Binary Log
→ 복제 및 시점 복구
18. Oracle에서는 어떻게 동작할까
Oracle은 일반적으로 다음 구조를 사용한다.
Online Redo Log
Redo Log Buffer
Undo Segment
Archived Redo Log
DB Buffer Cache
Data File
Control File
장애가 발생하면 Oracle은 Online Redo Log를 이용해 데이터 파일에 반영되지 않은 변경을 Roll Forward한다.
이 과정에는 커밋된 변경뿐 아니라 미커밋 변경이 포함될 수 있다.
이후 Undo 정보를 이용해 미커밋 트랜잭션을 롤백한다.
Instance Recovery
1. Redo 적용
2. 데이터 블록을 장애 직전 상태로 복구
3. 데이터베이스 Open
4. 미커밋 트랜잭션 Undo
Oracle은 일부 Undo 작업이 완료되기 전에 데이터베이스를 먼저 열고, 필요에 따라 백그라운드 또는 온디맨드 방식으로 트랜잭션 복구를 계속 수행할 수 있다.
19. PostgreSQL에는 전통적인 Undo 로그가 없다
Redo와 Undo를 모든 DBMS에 동일하게 적용해서는 안 된다.
PostgreSQL은 Oracle이나 InnoDB처럼 전통적인 별도 Undo 로그를 사용하지 않는다.
PostgreSQL은 UPDATE 시 기존 튜플을 직접 덮어쓰는 대신, 새로운 튜플 버전을 생성한다.
기존 Tuple
xmin = TX100
xmax = 없음
balance = 50,000
UPDATE 이후
기존 Tuple
xmin = TX100
xmax = TX200
balance = 50,000
새 Tuple
xmin = TX200
xmax = 없음
balance = 40,000
TX200이 커밋되지 않은 상태로 장애가 발생하면, 트랜잭션 상태를 기준으로 새 튜플은 보이지 않는 버전으로 처리된다.
이후 불필요한 튜플 버전은 Vacuum 과정에서 정리된다.
따라서 일반적인 원리는 다음과 같이 이해해야 한다.
공통 목적
커밋된 변경은 보존한다.
미커밋 변경은 사용자에게 보이지 않게 한다.
하지만 이를 구현하는 방식은 다르다.
Oracle / InnoDB
Redo + Undo 구조
PostgreSQL
WAL + MVCC Tuple Version + Transaction Status
20. 운영 환경에서 확인해야 할 설정
Redo와 Undo의 개념은 단순한 이론이 아니라 DB 운영 설정과 직접 연결된다.
로그 Flush 정책
COMMIT 시 로그를 어느 수준까지 디스크에 동기화할 것인지에 따라 성능과 데이터 안정성이 달라질 수 있다.
로그를 매번 동기화하면 내구성은 높아지지만 쓰기 지연이 증가한다.
반대로 비동기화 또는 주기적 Flush를 사용하면 처리량은 높아질 수 있지만, OS나 서버 장애 시 최근 커밋 데이터가 유실될 가능성이 생긴다.
Checkpoint 주기
체크포인트가 지나치게 자주 발생하면 디스크 쓰기 부하가 증가할 수 있다.
반대로 너무 드물면 장애 복구 시간이 늘어난다.
Redo 로그 크기
Redo 로그 용량이 너무 작으면 Checkpoint가 자주 발생해 쓰기 성능이 저하될 수 있다.
용량이 지나치게 크면 장애 복구 시 확인해야 할 로그 범위가 증가할 수 있다.
Undo 보존 기간
Undo를 너무 빨리 제거하면 장시간 실행되는 조회가 필요한 과거 버전을 읽지 못할 수 있다.
Oracle에서는 대표적으로 ORA-01555: snapshot too old 문제가 발생할 수 있다.
디스크 성능
쓰기 중심 DB에서는 데이터 파일뿐 아니라 로그 디바이스의 지연 시간도 중요하다.
특히 COMMIT 응답 시간이 로그 Flush를 기다리는 구조라면 Redo 로그 디스크의 fsync 지연이 트랜잭션 지연에 직접 반영된다.
운영 환경에서는 다음 지표를 함께 확인하는 것이 좋다.
Commit latency
Log flush latency
Redo generation rate
Checkpoint duration
Dirty page ratio
Buffer pool hit ratio
Disk fsync latency
Undo tablespace usage
Long-running transaction
Crash recovery duration
21. 흔히 하는 오해
오해 1. COMMIT하면 데이터 파일에 바로 기록된다
COMMIT은 일반적으로 데이터 페이지 전체의 디스크 기록을 의미하지 않는다.
커밋 결과를 복구할 수 있는 로그가 안정적으로 기록됐음을 의미하는 경우가 많다.
오해 2. 커밋하지 않은 데이터는 디스크에 기록되지 않는다
커밋되지 않은 변경이 포함된 Dirty Page도 데이터 파일에 기록될 수 있다.
그래서 Undo가 필요하다.
오해 3. Redo는 커밋된 트랜잭션만 재실행한다
개념적으로는 커밋된 결과를 살리기 위한 것이지만, ARIES 계열 복구에서는 장애 직전 상태를 재현하기 위해 미커밋 트랜잭션 변경까지 Redo한 후 Undo할 수 있다.
오해 4. Undo는 장애 복구에만 사용된다
Undo는 일반 롤백뿐 아니라 MVCC 일관성 읽기에도 사용된다.
오해 5. Redo 로그가 있으면 백업이 필요 없다
Redo는 현재 데이터 파일을 기준으로 한 Crash Recovery에 주로 사용된다.
데이터 파일 자체가 손상되거나 삭제되면 백업과 아카이브 로그가 필요하다.
오해 6. 모든 DB가 동일한 Redo·Undo 구조를 가진다
Oracle, MySQL InnoDB, PostgreSQL은 같은 ACID 목표를 서로 다른 내부 구조로 구현한다.
22. 전체 흐름 정리
하나의 UPDATE가 실행되고 장애가 복구되는 과정을 정리하면 다음과 같다.
[정상 처리]
1. 데이터 페이지를 메모리로 읽는다.
2. 변경 전 정보를 Undo에 기록한다.
3. 메모리의 데이터 페이지를 변경한다.
4. 변경 내용을 Redo에 기록한다.
5. COMMIT 시 필요한 로그를 디스크에 Flush한다.
6. 애플리케이션에 COMMIT 성공을 반환한다.
7. Dirty Page는 이후 데이터 파일에 기록한다.
[장애 발생]
1. 메모리의 Dirty Page가 유실된다.
2. 데이터 파일에는 일부 변경만 존재할 수 있다.
3. 로그를 이용해 장애 시점의 상태를 분석한다.
4. 필요한 변경을 Redo한다.
5. 미커밋 트랜잭션을 Undo한다.
6. 일관된 데이터 상태로 복구한다.
복구 알고리즘 관점에서는 다음 세 단계로 정리할 수 있다.
Analysis
→ 장애 시점의 트랜잭션과 Dirty Page 파악
Redo
→ 로그를 재생해 장애 직전 상태 재현
Undo
→ 커밋하지 않은 트랜잭션 제거
마무리
Redo와 Undo의 핵심 목적은 명확하다.
Redo
→ COMMIT된 변경을 잃지 않기 위한 복구 정보
Undo
→ 완료되지 않은 변경을 제거하기 위한 복구 정보
하지만 실제 장애 복구를 정확히 이해하려면 한 단계 더 나아가야 한다.
DB는 COMMIT할 때마다 데이터 파일을 즉시 저장하지 않는다. 대신 WAL 원칙에 따라 복구 로그를 먼저 안정적으로 기록하고, 데이터 페이지는 이후에 비동기로 저장한다.
또한 커밋되지 않은 변경도 데이터 파일에 먼저 기록될 수 있기 때문에 Undo가 필요하다.
대표적인 ARIES 계열 복구에서는 커밋 트랜잭션만 선택적으로 Redo하는 것이 아니라, 먼저 로그를 통해 장애 직전의 상태를 재현한 후 미완료 트랜잭션을 Undo한다.
결국 장애 복구의 본질은 다음 문장으로 정리할 수 있다.
DB는 Redo로 장애 직전까지의 변경 이력을 재현하고,
Undo로 완료되지 않은 트랜잭션의 흔적을 제거한다.
이 구조를 통해 DB는 애플리케이션이 성공으로 인식한 트랜잭션은 보존하고, 완료되지 않은 트랜잭션은 존재하지 않았던 것처럼 되돌릴 수 있다.
참고 자료
- Oracle Database, What Is the Redo Log?
- Oracle Database, What Is Undo?
- Oracle Database, Backup and Recovery Overview
- MySQL 8.4 Reference Manual, The InnoDB Storage Engine
- PostgreSQL Documentation, Write-Ahead Logging
- PostgreSQL Documentation, WAL Configuration
- C. Mohan et al., ARIES: A Transaction Recovery Method Supporting Fine-Granularity Locking and Partial Rollbacks Using Write-Ahead Logging, ACM TODS, 1992
'나의 주니어 개발 일기 > DB' 카테고리의 다른 글
| DB의 MVCC는 어떻게 동시성을 보장하는가? (0) | 2026.07.29 |
|---|---|
| Supabase 도커(Docker)로 로컬 개발 환경 구축하기 (0) | 2026.02.02 |
| DB 이중화와 MySQL Replication 구조 정리 및 실무 도입 방향성 (0) | 2025.09.05 |
| 샤딩과 파티셔닝, 무엇이 다르고 언제 써야 할까? (1) | 2025.08.29 |
| 커버링 인덱스, 클러스터드 인덱스, 넌클러스터드 인덱스 (1) | 2024.07.26 |
