만일 TX의 진행 도중 예상치 않게 Crash가 발생했다면 ( 컴퓨터가 꺼진다던지) 어떻게 해야할까?
DBMS의 Recovery Manager 컴포넌트가 log를 통해 crash를 복구해 줄 것이다.
ACID
DBMS의 보장 사항
- Atomicity : TX의 모든 action은 일어나거나 안일어나거나. 즉, 절반만 한다던지는 있을 수 없다.
- Cosistency : 일관성을 보장해야한다
- Isolation : 각 TX의 실행은, 다른 TX의 영향을 받지 않는다.
- Durability : TX이 커밋됬다면, 이는 Disk에 영구적으로 반영되어야 한다
Recovery Manager의 목표는 Atomicity와 Durability를 보장한다.
반면에, Buffer Manager의 목표는 response time의 최소화와 throughput의 최대화이다. 두 Manager간의 목표가 상충되는 부분이 존재하므로 , 이를 어떻게 해결하는지 알아보자
Motivation
- Atomicity : 만일 TX이 abort되어서 롤백되었다면?
- Duraibility : 만일 DBMS가 비정상 종료되었다면

인터리빙한 TX들이 진행중 Crash가 발생한 상황
- T4와 T5의 TX 진행중 crash 발생
- T1,T2,T3는 Disk에 영속적으로 저장되어야한다 (즉, 아직 write된 데이터가 버퍼에 남아있다든지의 이유로 Disk에 반영이 안되어있으면 안된다)
- T4,T5는 undo되어야 한다 (TX은 atomic해야하므로 , 진행중 Crash 되었다면 Undo되어야 한다)
- 리커버리 매니저가 log를 통해 해당 작업을 수행한다
버퍼풀 관리

TX 처리 시 , 데이터 기록에 관한 옵션
- Force
- TX 완료 후 , 데이터를 바로 디스크에 저장
- Response time이 감소한다.
- Durability를 제공한다.
- Steal
- TX이 Commit되지 않은 상황에서 , Frame을 교체한다.
- TX이 진행중인데 참조하는 frame이 교체 정책에 의해서 교체된 경우
- throughput이 증가한다.
- Atomicity를 제공하기 어렵다.
No Force , Steal -> 버퍼매니저가 원함 ( 처리율 증가 및 반응 시간 감소 )
Force , No Steal -> 리커버리매니저가 원함 ( Atomicity 및 Durability 보장 )
기본적으로 DBMS는 No Force , Steal 을 따르므로 리커버리 매니저가 Atomicity와 Durability를 보장할 수 있는 방법을 찾아야한다.
Force/Steal Detail

- Steal ( 왜 Atomicity를 보장하기 힘들까?)
- 작업중인 TX이 lock을 소유하고 있는 Frame이 Disk에 쓰였다.
- 만약, Frame에 대한 lock을 소유하고 있는 TX이 abort되면?
- UNDO를 통해 해결한다
- No Force ( 왜 Durability를 보장하기 힘들까?)
- 만일 , 변경된 Frame이 Disk에 쓰이기 전 ( 즉, buffer pool에만 존재)에 system에 장애가 발생한다면?
- REDO를 통해 해결한다
기본적 아이디어 : 로그
리커버리 매니저가 변경 전,후의 데이터를 logging 한다.
로그는 REDO/UNDO action들의 순서 list이다.

위와 같이 Log record의 list의 저장을 통해 Redo/Undo가 가능하다.
Write Ahead Log Protocol

WAL 프로토콜
데이터의 변경 전, Log를 선행하여 기입
- 연관된 data page가 disk에 기록되기 전, 반드시 log 레코드를 업데이트
- TX의 commit 전, log를 기록
WAL 상세

- 각 log 레코드는 유니크한 log sequence number (LSN) 을 소유한다.
- Crash Recovery를 위해 , 데이터 베이스에 존재하는 각 페이지는 해당 페이지의 가장 최근에 일어난 변경을 기술하는 LSN을 소유한다. 해당 LSN을 page LSN(Datablock에 반영된 가장 마지막 로그 번호)이라고 한다.
- System은 flushedLSN( disk에 반영된 가장 최근 LSN)을 가지고 있다.
로그 레코드 구조 및 로그 테이블

-
로그레코드의 필드
- prevLSN : 현재 log 이전의 가장 최근 log를 포인팅
- XID : TX의 id
- type : 일어난 action의 종류를 기입
그 외 여러 필드를 소유한다.
-
로그 레코드의 타입
- Update
- Commit
- Abort
- End ( commit이나 abort의 종료를 나타냄 )
- Compensation Log Records ( CLRs )

- TX Table
- 진행중인 TX을 entry로 소유
- XID와 Status ( running / commited / aborted) 그리고 lastLSN( TX안에서 일어나는 여러가지 read/write 중 가장 마지막에 일어난 action에 대한 log 번호) 포함
- Dirty Page Table
- buffer pool에 존재하는 dirty page를 entry로 소유
- recLSN을 소유
- recLSN은 해당 페이지를 가장 최근에 write한 최신 로그의 번호
TX의 실행
TX 실행 시 일어나는 일련의 작업
- 일련의 read와 write , 이후로 commit과 abort로 마무리
- Strict 2PL
- Steal , No Force 버퍼관리
- Write Ahead Log 기법 사용
- TX이 일어나기 전 , 로그를 미리 기록 ( redo 와 undo를 위해 )
체크포인팅
System의 장애로부터 recovery time을 최소화하기위해 주기적으로 DBMS는 체크포인트를 생성한다.
- Begin_checkpoint 레코드
체크포인트의 시작점(begin_checkpoint 레코드)을 기록하고 로그에 포함한다.
- End_checkpoint 레코드 ( fuzzy checkpoint )
begin_checkpoint당시의 TX table과 dirty page table을 End_checkpoint에 기록하고 로그에 포함한다.
- 체크포인트 레코드의 LSN을 safe place에 보관한다. (master record)
시스템의 장애 발생 이후,
저장되는 자료구조 정리

간단한 TX Abort 알고리즘
TX abort에 대해 알아보자 , Crash 발생 없이 abort를 한다고 가정한다.
Update를 undo하고 log를 통해 'play back'하고 싶다.
- TX table의 TX들의 lastLSN 번호를 알아낸다.
- 레코드의 'prevLSN' 필드를 통해서 , Chain 방식으로 로그 레코드들을 찾아나갈 수 있다.
- Undo 시작 전 , Abort log record를 기록한다.
- Undo 중 crash가 일어났을 경우를 대비하기위해!
- Undo 시작 전, data에 대한 lock을 걸어야한다.
- Page에 예전 값을 write 하기 전에 CLR을 작성한다.
- Undo중 fail 시에 , CLR을 통해 어디서부터 다시 Undo할지 결정가능
- CLR에는 undonextLSN이라는 필드가 존재한다.
- Undo의 마지막에 end log 레코드를 작성한다.
TX Commit 알고리즘
- log에 commit을 작성한다.
- TX의 lastLSN까지 Disk에 flush된다.
- 이로서 flushedLSN이 lastLSN임을 보장한다.
- Commit()을 반환한다.
- end 레코드를 log에 기록한다.
크래시 리커버리 개요
장애 복구상황의 개요를 살펴보자
우선 장애발생시 master record로부터 체크 포인트를 찾는다. 그 후, crash recovery가 시작된다.

- Last 체크포인트로부터 Crash가 발생한 지점을 Analysis
- history를 참조하여 모든 action을 redo한다 ( Commit TX들에 대해서 )
- fail한 TX에 대하여 Undo를 진행한다 ( abort TX들에 대해서 )
크래시 리커버리 순서도
Analysis 단계
- Checkpoint를 통해서 State를 재구성한다.
- END_CHECKPOINT 레코드를 이용한다.
- 체크포인트부터 크래시 시점까지의 로그들을 스캔한다.
- END타입 로그 레코드
- 해당 TX을 TX table에서 삭제. TX이 end로 끝났기 때문에 active하지 않기 때문이다.
- 다른 타입 로그 레코드
- TX을 TX table에 추가해준다.
- 해당 TX의 lastLSN을 현재 로그 레코드의 LSN으로 설정한다.
- 만일 로그 레코드 타입이 commit이라면 TX 엔트리의 state를 commit으로 설정한다.
- 페이지 P에 대한 UPDATE 레코드
- 만약 해당 Page가 Dirty Page Table에 없을경우
- 만약 DPT에 해당 페이지가 없다면 추가
- recLSN = LSN 으로 세팅
Redo 단계
- repeat history를 통해 Crash 당시의 State 재구성
- 모든 Update를 재적용 ( abort된 TX 까지 ) , CLR들을 Redo
- Dirty Page Table 안에서 가장 작은 recLSN을 포함하고 있는 Log 레코드를 찾는다( 가장 오래된 dirty page). 각 CLR 혹은 update log의 recLSN에 대하여 다음 케이스가 되기 전까지 반복적으로 REDO를 한다.
- Crash의 영향을 받은 page가 더이상 Dirty Page Table에 없는 상태
- Crash의 영향을 받은 page가 Dirty Page Table에 존재하지만 , 해당 페이지의 recLSN > LSN 이거나 pageLSN>LSN 인 상태
- Redo action
- log된 action을 재적용한다
- pageLSN을 적용한 action의 log로 세팅한다. 추가적인 log를 기록하지 않는다.
Undo 단계

ToUndo 집합이 공집합이 될 때까지 다음을 반복한다.
- ToUndo 집합에서 가장 큰 LSN을 찾는다.
- 만약 해당 LSN이 CLR이고 undonenextLSN == NULL 이라면
- 만약 해당 LSN이 CLR이고 undonenextLSN != NULL 이면
- undonenextLSN을 ToUndo에 추가한다.
- 만약 해당 LSN이 Update일 경우.
- Update를 Undo한다.
- CLR을 작성한다.
- prevLSN을 ToUndo에 추가한다.
크래시 리커버리 예제

