한양과학기술고등학교

에듀테크 프로젝트 대시보드

WAL (Write-Ahead Logging)

WAL — Write-Ahead Logging

은행 앱에서 계좌 A에서 B로 송금 도중 서버 전원이 나갔다. A는 빠졌는데 B는 안 들어왔다면? 데이터베이스는 데이터 페이지를 직접 고치기 전에, 무엇을 바꿀지 먼저 로그에 적고 그 로그를 디스크에 확실히 기록(fsync)한다. 이것이 Write-Ahead Logging 규칙이다. 데이터 페이지는 느긋하게 나중에 디스크로 내려도 된다 — 크래시가 나면 로그를 다시 읽어 커밋된 것은 재실행(redo), 안 끝난 것은 되돌리기(undo) 하면 되기 때문이다.

한 가지 규칙: 로그가 데이터보다 먼저

1. 로그 레코드

<T1, A, 100 → 80> 를 로그 파일 끝에 순차 추가하고 fsync() 로 디스크에 확정. 순차 쓰기라 빠르다.

2. 데이터 페이지

버퍼 풀(메모리)에서만 값을 바꾸고 dirty 표시. 디스크 반영은 체크포인트 때 몰아서. 크래시로 날아가도 로그로 복원.
Redo 로그는 “바뀐 결과(after image)”를, Undo 로그는 “원래 값(before image)”을 담는다. 실제 DB(ARIES)는 둘 다 한 레코드에 넣어, 커밋된 트랜잭션은 redo로 앞으로, 안 끝난 트랜잭션은 undo로 뒤로 감는다.

크래시 & 복구 시뮬레이터

트랜잭션을 시작해 계좌 값을 바꾸고, 커밋하기 전이든 후든 아무 때나 💥 크래시를 눌러 보라. 버퍼 풀(메모리)은 전부 사라지지만 로그와 이미 디스크에 내려간 페이지는 남는다. 복구 실행을 누르면 로그만으로 정확한 상태를 되찾는다.

WAL 로그 순차 추가 · 항상 디스크에 fsync

계좌 페이지 버퍼 풀 (메모리)

디스크에 확정된 값 체크포인트 때 반영

복구는 3단계 (ARIES)

1. 분석 (Analysis)

마지막 CHECKPOINT 부터 로그를 앞으로 읽으며, 커밋 레코드가 있는 트랜잭션(승자)과 없는 트랜잭션(패자) 목록을 만든다.

2. 재실행 (Redo)

체크포인트 이후의 모든 UPDATE를 로그 순서대로 디스크에 다시 적용한다 (“역사 반복”). 이미 반영된 것은 LSN 비교로 건너뛴다.

3. 되돌리기 (Undo)

패자 트랜잭션의 변경을 로그를 거꾸로 읽으며 before image로 복원하고, 보상 레코드 CLR 을 남긴다.

트랜잭션 커밋의 핵심 코드

# 값을 바꾸기 전에 — 로그부터 (Write-Ahead) def update(txn, page, key, new_val): rec = LogRecord(lsn=next_lsn(), txn=txn.id, page=page.id, key=key, before=page[key], after=new_val) log.append(rec) # 1) 로그 버퍼에 추가 page[key] = new_val # 2) 그 다음에야 데이터 페이지 수정 (메모리) page.dirty = True page.recLSN = min(page.recLSN, rec.lsn) def commit(txn): log.append(LogRecord(lsn=next_lsn(), txn=txn.id, type="COMMIT")) log.fsync() # ★ 커밋 로그가 디스크에 닿아야 "커밋 완료" return "OK" # 데이터 페이지는 아직 디스크에 없어도 됨
durability의 진짜 의미: 사용자가 “완료” 응답을 받은 시점 = 커밋 로그 레코드가 디스크에 fsync 된 시점. 데이터 파일은 몇 초 뒤에 써져도 크래시 복구가 메꿔 준다.

더 알아두면 좋은 것

그룹 커밋 — 매 커밋마다 fsync 하면 디스크가 병목. 여러 트랜잭션의 커밋 로그를 모아 한 번에 fsync 해 처리량을 올린다.

체크포인트 트레이드오프 — 자주 하면 복구는 빠르지만 평소 I/O 부담이 커지고, 드물게 하면 반대. 보통 dirty 페이지가 일정량 쌓이면 조금씩 흘려보낸다(fuzzy checkpoint).

로그가 곧 진실 — PostgreSQL WAL, MySQL redo log(ib_logfile), SQLite -wal 파일, 그리고 복제(replication)도 이 로그를 그대로 다른 서버로 보낸다.

LSN (Log Sequence Number) — 로그 레코드마다 붙는 단조 증가 번호. 페이지에 “마지막으로 반영한 LSN”을 적어 두면 redo 때 중복 적용을 피할 수 있다(멱등).