7장. Redo · 커밋 · 인스턴스 복구
전원이 뽑혀도 커밋된 데이터는 살아남는다 — 그 약속을 지키는 것이 redo입니다. COMMIT 한 번이 디스크까지 가는 여정과, 크래시 후 Oracle이 스스로 일어서는 2단계 복구를 봅니다.
① redo의 생성 경로 — log buffer → LGWR → online redo log ② COMMIT의 실제 동작과 log file sync 대기 ③ log switch · ARCHIVELOG · checkpoint ④ 인스턴스 크래시 복구 2단계: roll forward(redo 재생) → rollback(undo로 미커밋 취소)
7.1 Redo란 — 모든 변경의 일기장
6장의 undo가 "되돌리기 위한" 데이터라면, redo는 "다시 하기(re-do) 위한" 데이터입니다. 데이터 블록에 가해지는 모든 변경은 그 변경을 재현할 수 있는 redo record로 먼저 기록됩니다. 테이블 블록 변경도, 인덱스 블록 변경도, 심지어 undo 블록 변경도 redo에 남습니다.
왜 이런 이중 기록을 할까요? Buffer Cache의 변경된(dirty) 블록을 매번 디스크에 쓰는 것은 느립니다 — 블록이 datafile 여기저기에 흩어져 있어 랜덤 I/O이기 때문입니다. 반면 redo는 로그 파일 끝에 이어 쓰기만 하는 순차 I/O라 빠릅니다. 그래서 Oracle의 전략은:
"변경의 기록(redo)만 빠르게 디스크에 확정하고, 실제 블록 쓰기는 DBWn이 나중에 천천히." 크래시가 나도 일기장(redo)만 있으면 블록을 다시 만들 수 있으니까요. PostgreSQL의 WAL(Write-Ahead Log)과 완전히 같은 철학입니다 — PostgreSQL 9장과 비교해 보세요.
redo가 흐르는 경로는 세 정거장입니다.
- Redo Log Buffer — SGA 안의 작은 원형 버퍼. 서버 프로세스가 redo record를 일단 여기에 씁니다.
- LGWR(Log Writer) — log buffer의 내용을 디스크로 내리쓰는 전담 백그라운드 프로세스(4장).
- Online Redo Log 파일 — 디스크 위의 redo 최종 목적지. 최소 2개의 그룹이 돌아가며 쓰입니다.
7.2 COMMIT 한 번의 여정 — 커밋은 왜 빠른가
많은 분들이 오해합니다. "커밋하면 변경된 데이터가 디스크에 저장된다"고요. 사실 커밋 시점에 디스크에 확실히 쓰이는 것은 데이터 블록이 아니라 redo입니다.
UPDATE 실행 시점: 데이터 블록은 Buffer Cache에서 dirty 상태가 되고, 변경 내용의 redo record는 Redo Log Buffer에 쌓입니다. 아직 전부 메모리 위입니다.
COMMIT! 트랜잭션에 커밋 SCN이 확정되고, 커밋 기록이 redo record로 log buffer에 추가됩니다.
LGWR가 log buffer의 내용을 online redo log 파일에 순차 쓰기합니다. 이 디스크 쓰기가 끝날 때까지 세션은 log file sync 이벤트를 대기합니다 — 커밋 응답 시간의 정체가 바로 이것.
쓰기 완료! 세션은 "커밋 성공"을 받습니다. 이 순간부터 전원이 뽑혀도 이 트랜잭션은 살아남습니다 — redo가 디스크에 있으니까요(내구성, Durability).
반전: 정작 데이터 블록은 아직 디스크에 안 쓰였습니다. DBWn이 나중에 체크포인트 등 계기로 천천히 씁니다. 커밋이 데이터 양과 무관하게 빠른 이유 — 디스크에 쓰는 건 작은 redo뿐이기 때문입니다.
LGWR가 log buffer를 디스크로 내리는 계기는 커밋만이 아닙니다.
- 세션이 COMMIT(또는 ROLLBACK)할 때
- 3초마다 주기적으로
- log buffer가 1/3 찼거나 쌓인 redo가 1MB에 달했을 때
- DBWn이 dirty 블록을 쓰기 직전 — 블록보다 그 블록의 redo가 먼저 디스크에 있어야 한다는 write-ahead 규칙 때문
루프 안에서 1건 처리할 때마다 커밋하면, 매번 LGWR의 디스크 쓰기를 기다립니다. 10만 건이면 10만 번의 동기 I/O 대기죠. 배치 작업은 모아서 커밋하는 것이 기본입니다. AWR에서 log file sync가 상위 대기 이벤트라면 과도한 커밋 빈도나 느린 redo 디스크를 의심하세요.
7.3 Log Switch와 ARCHIVELOG — 일기장 돌려쓰기
online redo log는 최소 2개의 그룹이 원형으로 돌아가며 쓰입니다. 현재 그룹이 가득 차면 다음 그룹으로 넘어가는 것이 log switch입니다. 그런데 한 바퀴 돌아 다시 첫 그룹을 덮어써야 할 때, 그 안의 redo가 지워지면 그 시점 이전으로의 복구 능력도 사라집니다. 그래서 운영 DB는 ARCHIVELOG 모드를 켜고, 덮어쓰기 전에 ARCn 프로세스가 사본(archived log)을 떠 둡니다.
LGWR는 Group 1(CURRENT 상태)에 redo를 쓰고 있습니다. 그룹들은 원형 큐처럼 순서대로 돌려쓰입니다.
Group 1이 가득 찼습니다! log switch — LGWR는 즉시 Group 2로 펜을 옮깁니다. Group 1은 "복구에 아직 필요할 수 있는" ACTIVE 상태가 됩니다.
ARCHIVELOG 모드라면 ARCn 프로세스가 Group 1의 사본을 archived log로 복사합니다. 이 사본들이 쌓여 백업 이후의 모든 변경 기록이 되고, PITR(특정 시점 복구)의 재료가 됩니다.
순환 계속: Group 2 → Group 3 → 다시 Group 1... 단, 아카이브가 끝나지 않은 그룹은 덮어쓸 수 없습니다. 아카이브가 밀리면 DB 전체가 멈추는 이유입니다.
-- redo log 그룹 상태 확인
SELECT group#, status, bytes/1024/1024 AS mb, archived
FROM v$log;
GROUP# STATUS MB ARCHIVED
------ -------- --- --------
1 INACTIVE 200 YES ← 아카이브 완료, 재사용 가능
2 CURRENT 200 NO ← LGWR가 지금 쓰는 중
3 ACTIVE 200 YES ← 체크포인트 미완료, 아직 복구에 필요
-- 아카이브 모드 확인 / 수동 log switch
ARCHIVE LOG LIST;
ALTER SYSTEM SWITCH LOGFILE;
archived log를 쓸 디스크가 가득 차면 ARCn이 멈추고, 아카이브 안 된 그룹은 재사용할 수 없으므로 결국 LGWR도 멈춥니다. 증상은 모든 세션이 매달리는 대기와 alert log의 "all online logs needed archiving". 아카이브 영역 공간 모니터링은 Oracle 운영의 기본 중 기본입니다.
7.4 Checkpoint — 복구 시작선을 앞으로 당기기
Checkpoint는 "여기까지의 변경은 datafile에 확실히 반영되었다"는 동기화 지점입니다. DBWn이 그 시점까지의 dirty 블록을 디스크에 쓰고, CKPT 프로세스가 컨트롤 파일과 datafile 헤더에 체크포인트 위치(SCN)를 기록합니다.
체크포인트가 중요한 이유는 단 하나 — 크래시 복구를 어디서부터 시작할지를 정하기 때문입니다. 체크포인트 이전의 redo는 이미 datafile에 반영됐으니 재생할 필요가 없습니다. 체크포인트가 자주 진행될수록 복구는 빨라지고, 대신 평상시 디스크 쓰기는 늘어납니다. 이 균형은 FAST_START_MTTR_TARGET(목표 복구 시간, 초 단위) 파라미터로 조절합니다.
7.5 인스턴스 복구 — 크래시에서 스스로 일어서는 2단계
전원 장애로 인스턴스가 즉사했다고 합시다. 그 순간 디스크의 상태는 엉망입니다.
- 커밋됐지만 datafile에는 아직 안 쓰인 변경이 있고 (dirty 블록은 메모리와 함께 증발),
- 반대로 커밋 안 됐는데 datafile에 이미 쓰인 변경도 있습니다 (DBWn은 커밋 여부와 무관하게 씁니다).
다음 기동 때 SMON이 이 둘을 정리합니다. 무기는 redo와 undo, 순서는 정확히 2단계입니다.
⚡ 크래시 직후의 datafile: 커밋된 TX1의 변경은 파일에 없고(dirty 블록이 메모리째 증발), 미커밋 TX2의 변경은 파일에 있는(DBWn이 먼저 써버림) 모순 상태입니다.
재기동. SMON이 컨트롤 파일의 체크포인트 위치를 보고 "여기부터 정리가 필요하다"를 파악합니다. 무기는 online redo log — 체크포인트 이후의 모든 변경 기록이 담겨 있습니다.
1단계 roll forward(전진): 체크포인트 위치부터 redo를 순서대로 재생합니다. 커밋 여부를 따지지 않고 전부 재적용 — TX1도, TX2도, 심지어 undo 블록의 변경까지 크래시 직전 상태로 복원됩니다.
2단계 rollback(후진): 이제 6장의 undo가 등판합니다. roll forward로 되살아난 undo에서 TX2의 before image를 꺼내 미커밋 변경을 취소합니다. (DB는 roll forward만 끝나면 열리고, rollback은 열린 뒤에도 백그라운드로 이어집니다.)
결과: 커밋된 것은 전부 살아남고, 미커밋은 흔적 없이 사라졌습니다. redo가 내구성(D)을, undo가 원자성(A)을 책임진 셈입니다. DBA가 한 일은? 그냥 startup 한 번.
undo 자체도 데이터 블록이라(6장) 크래시로 최신 undo가 디스크에 없을 수 있습니다. 그래서 redo 재생으로 undo부터 되살린 다음에야 그 undo로 미커밋을 되돌릴 수 있습니다. "redo가 undo를 복원하고, undo가 트랜잭션을 되돌린다" — 이 한 문장이 Oracle 복구의 전부입니다.
7.6 PostgreSQL WAL과 나란히 놓고 보기
같은 write-ahead 철학이라 대응 관계가 깔끔합니다. PostgreSQL을 아는 분이라면 이 표 하나로 이 장을 복습할 수 있습니다 (PostgreSQL 9장 WAL과 Checkpoint).
| 역할 | Oracle | PostgreSQL |
|---|---|---|
| 변경 로그 | redo (online redo log) | WAL 세그먼트 |
| 로그 쓰기 프로세스 | LGWR | walwriter (+ 커밋 시 백엔드 직접) |
| 커밋 대기 이벤트 | log file sync | WALWrite / fsync 대기 |
| 로그 파일 운용 | 고정 그룹 순환 + 아카이브 | 세그먼트 생성·재활용 + archive_command |
| 미커밋 취소 정보 | undo (별도 저장소, 복구 2단계에 필수) | 불필요 — 옛 버전이 heap에 남아 있어 롤백이 저렴 |
| 크래시 복구 | roll forward(redo) → rollback(undo) | REDO 지점부터 WAL 재생 (undo 단계 없음) |
가장 큰 차이는 마지막 줄입니다. PostgreSQL은 미커밋 변경도 "새 버전 튜플"일 뿐이라 복구에서 되돌릴 것이 없습니다(커밋 안 된 XID의 튜플은 그냥 안 보임). Oracle은 제자리 수정 구조라 undo를 이용한 rollback 단계가 반드시 필요합니다 — 6장에서 본 구조 차이가 복구 절차의 차이로 그대로 이어지는 것이죠.
✍️ 이해도 체크
Oracle Database 19c Concepts Guide: Physical Storage Structures (online redo log 구조) · Oracle Database Instance (인스턴스 복구 절차와 체크포인트) — SCN, thread, 멀티플렉싱 등 심화 내용이 있습니다.