- Published on
로그 기반 복구 작업
- Authors
- Name
- Key Concents
- Name
- 어떤 경우에 복구가 필요한가?
- Name
- Data Access
- Name
- Stable Storage
- Name
- 로그 기반 회복
- Name
- 즉시 갱신과 지연갱신
- Name
- Commit
- Name
- Undo와 Redo 작업
- Name
- Check Point
- Name
- Recovery Algorithm
- Name
- 트랜잭션 롤백
- Name
- CLR를 쓰는 이유
- Name
- 장애로 인한 재시작
- Name
- Redo Phase에 대한 의문점
- Name
- 미디어 손실로부터의 복구
- Name
- Log Record Buffering
- Name
- Database Buffering
- Name
- Buffer Management
- Name
- Fuzzy Checkpointing
- Name
- Dump
- Name
- 참고
Tags
Previous Article
Next Article
Key Concents
로그 기반 복구 작업을 간단히 모델링해보고 일어나는 시나리오를 상세히 살펴보도록 할 것이다. 이를 위해 먼저 몇 가지 개념들을 정리하고 시작하자.
어떤 경우에 복구가 필요한가?
복구 작업을 다루기 전에 먼저 어떤 경우에 복원작업이 이루어질지 간단히 생각해보자.
트랜잭션을 복구하는 경우는 크게 3가지로 트랜잭션 실패, 시스템의 충돌로 강제종료, 미디어 손실이 있다.
트랜잭션이 실패했다는 말은 말 그대로 트랜잭션이 어떤 이유(무결성 제약 조건 등 사용자의 의도적 에러 조건 혹은 데드락 등 DBMS 시스템적인 에러 조건)로 완료될 수 없어서 중단된 경우를 말한다.
시스템 충돌은 여러 케이스가 있는데 대략적으론 의도치 않게 DBMS 프로세스가 강제 종료된 상황을 생각하면 될 것 같다. 예컨데 컴퓨터 전력이 아예 끊기거나, 하드웨어 고장, 혹은 운영체제 차원 등에서 어떤 충돌이 일어난 경우를 생각할 수 있을 것이다.
미디어 손실은 디스크의 데이터 자체가 최소 부분적으로 손실된 경우를 의미한다.
시스템 충돌과 미디어 손실은 어떻게 분류를 하긴 했지만, 생각보다 그 원인이나 시나리오를 명확히 하기 어려워보인다.
이 때문에 복구작업을 설계하기 위해 최소한의 어떤 전제조건을 깔고들어가는데 이는 다음의 두 가지와 같다.
시스템 충돌 상황에서 non-volatile storage의 데이터가 손실되지 않는다고 가정하고 이를 Fail-Stop Assumption이라고 부른다.(실제 DBMS는 disk data의 corruption을 감지하기 위해 많은 무결성 검사를 수행한다.)
미디어 손실 상황에서는 어떤 데이터가 손실되었는지 항상 찾아낼 수 있다고 가정한다.
(이는 disk drive의 checksum을 이용해 탐지한다.)
Data Access
Physical block는 디스크에 위치한 블록(페이지)를 말하며, Buffer block은 메모리에 위치한 블록(페이지)를 말한다.
두 위치에 있는 블록을 동기화하기 위해 다음의 두 가지 Operation을 정의하자.
: Physical Block 를 메인메모리로 전달한다.
: Buffer Block 를 디스크로 전달한다.
각각의 트랜잭션 는 각각 고유한 공간(Isolation을 위해)을 가지고, 자신이 접근하고 변경한 데이터들의 복사본을 가지고 있다.
: 데이터 X의 정보를 의 고유 복사본 을 저장한다.
: 의 데이터 복사본인 를 데이터 에 저장한다.
따라서 에서 어떤 데이터 X에 접근하고 수정하고 데이터베이스에 반영하는 과정은 대략적으로 다음 과정으로 생각할 수 있다.
설명 | ||||
---|---|---|---|---|
초기 | ||||
데이터 가 저장된 block B는 먼저 메인메모리에 적재되어 있어야만 한다. | ||||
데이터 를 의 로컬 영역으로 복사한다. | ||||
데이터 를 수정한다. | ||||
변경정보를 Buffer Block에 반영한다. | ||||
수정된 Buffer를 디스크에 출력한다. |
이 때 주의할 점은 나 이 반드시 의 직전이나 직후에 이루어지는 것은 아니라는 점이다.
우선은 대략적으로 계층적인 데이터 접근이 이루어진다는 점만 숙지하고 넘어가도록 하자.
Stable Storage
다양한 장애에서 데이터베이스를 복구하기 위해선 무엇이 필요한가? 손실된 데이터를 복구하려면 어떤 형태로든 복구하기 위한 정보가 먼저 저장되어야만 한다.
Stable storage는 일종의 이상적인 저장소이다. 어떤 상황에서도 정보가 손실되지 않는 저장소이다. 단순히 Nonvolatile을 넘어 어떤 시스템 장애에서도 손실되지 않는 저장소라는 것이다.
시스템 장애 상황에서 데이터가 손실되었는데, 만약 이를 복구하기 위한 정보까지 손실된다면 데이터는 완전 손실될 뿐이다. 따라서 복구를 위한 정보를 저장하는 것은 stable storage가 되어야 할 것이다.
실제로 이상적인 stable storage를 구성하는 것은 불가능하기 때문에 이를 위해 다양한 방식의 구현(여러개의 copy 등)이 사용된다.
로그 기반 회복
현대의 데이터베이스 시스템은 대부분 복구를 위해 로그 기반의 알고리즘을 사용한다.
로그는 로그레코드의 연속(보통 물리적인 연속이라 생각해도 되는 것 같다.)이다. 로그레코드는 데이터베이스의 변경사항과 관련된 정보들을 저장하며, stable storage에 저장되어야 한다.
트랜잭션 가 시작되면 다음과 같은 로그레코드를 작성함으로써 트랜잭션이 시작되었음을 명시한다.
<>
만약 가 데이터 에 value 를 작성()한다고 하자.
이는 당연히 데이터베이스에 변경이 발생한다는 의미이기 때문에 로그레코드를 작성해야만 한다.
이 때에는 다음과 같은 레코드를 작성한다.
<>
또한 트랜잭션 가 완료되면 트랜잭션이 완료되었다는 것을 명시해야 할 것이다.
이 때에는 다음과 같은 레코드를 작성한다.
<>
즉시 갱신과 지연갱신
즉시갱신(immediate modification)과 지연갱신(deffered modification)은 커밋되지 않은 변경정보를 공용 블록(버퍼나 디스크)에 반영할 것인지 여부를 나타낸다.
이름에서 알 수 있듯 즉시갱신은 커밋 전에 데이터를 블록에 반영한다는 것을 의미하고
지연갱신은 커밋이 되기 전까지 모든 operation을 트랜잭션의 local area에서 수행한다는 것을 의미한다.
이전 글에서 얘기했듯이 지연갱신의 경우 모든 작업이 local area에서 수행되기 때문에 회복작업이 단순하지만, 그만큼 메모리 오버헤드를 감수해야한다는 것을 의미한다.
또한 즉시갱신의 경우 회복작업을 위해선 데이터를 블록에 쓰기 전에 반드시 로그레코드를 기록해야만 할 것이다.
Commit
트랜잭션 이 커밋되었다는 것은 당연히 로그레코드에 <>이 기록되었다는 것이며, 에 의해 변경된 모든 정보가 이미 로그레코드에 작성돼야만 한다.
이 때 변경된 정보가 로그레코드에 작성되었을 뿐 디스크에 완전히 반영된 상태가 보장되는 것은 아니다(NO-FORCE).
종합하면 다음의 표처럼 통해 커밋이 되기 전에 디스크블록에 데이터가 반영될 수도 있고,
커밋이 되고 나서 디스크블록에 데이터가 반영될 수도 있다는 것이다.
Log | Write | Output |
---|---|---|
<> | ||
<> | ||
<> | ||
| ||
<> | ||
<> | ||
<> | ||
<> | ||
Undo와 Redo 작업
: 에 의해 변경된 모든 데이터를 원상복구한다. 에 의해 생성된 로그레코드를 역순으로 훑으며 실행된다.
이 때 <>에 의해 변경된 데이터를 다시 복원할 경우 다음과 같은 레코드를 기록한다.
<>
모든 undo 작업이 끝나면 마지막으로 트랜잭션이 취소(완료)됐다는 의미로 다음과 같이 레코드를 기록한다.
<>
: 에 의해 기록된 로그레코드를 순서대로 훑으며 블록의 데이터를 새로운 값으로 업데이트한다. 이 경우 새로운 로그레코드를 작성하지 않는다.
Recovering from Failure
지금까지 정의한 개념들을 바탕으로 어떤 조건 하에 undo와 redo를 할지 생각해보자.
우리가 아는 것은 log record 뿐이다!
undo는 트랜잭션이 아직 완료되지 않은 상태로 중단된 경우에 실행되어야 한다.
즉, 시작은 했지만, 완료(Commit or Abort)되어서는 안된다는 것.
Transaction needs to be undone
if the log contains <>
but does not contain either the <> or <>
redo의 경우 트랜잭션이 완료되었을 때 실행되어야 한다. 따라서
Transaction needs to be redone
if the log contains <>
And contains the <> or <>
Check Point
만약 Undo와 Redo 작업에서 그동안의 모든 트랜잭션을 처리해야한다면 당연히 엄청난 시간이 걸릴 것이다.
또한 이미 디스크에 출력이 완료된 데이터에 대해서 Redo 작업을 또 다시 처리하는 경우도 생길 수 있다.
이러한 오버헤드를 방지하기 위해 체크포인트가 존재한다.
Check pointing을 수행하는 동안 다음과 같은 작업들이 수행된다.
- 현재 메인메모리에 존재하는 모든 로그레코드를 stable storage로 출력한다.
- 현재 메인메모리에 존재하는 모든 dirty block(아직 disk에 반영되지 않는 변경사항을 가진 buffer block)을 디스크로 출력한다.
- 로그레코드 <>을 기록한다. 이 때 은 현재 실행 중인 트랜잭션의 리스트
- Check point를 수행하는 동안 모든 변경작업은 중단된다.
복원 작업을 수행하는 동안 어떤 트랜잭션들이 복원의 대상이 될까? 체크포인트를 기준으로 생각해본다면...
먼저 가장 마지막 체크포인트 이후 시작한 트랜잭션들은 당연히 복원 작업의 대상이 될 것이다. 또한 체크포인팅을 하며 기록했던 리스트 이 있다.
- EoL(End of Log)로부터 역순으로 로그를 거슬러올라가며 마지막으로 기록된 checkpoint log <>을 찾는다.
- 에 포함된 트랜잭션과 <>이 기록된 이후 시작한 트랜잭션에 대해서 redo 혹은 undo가 필요한지 체크한다.
- <> 이전에 Commit 혹은 Abort된 모든 트랜잭션은 이미 체크포인팅을 통해 변경사항을 적어도 redo log로 stable storage에 기록해둔 상태이다.
Recovery Algorithm
이제 정말 복원 작업에 대해서 살펴보도록 하자.
트랜잭션 롤백
먼저 트랜잭션 중단으로 인한 복구작업(rollback)을 고려해보자.
트랜잭션 가 다음과 같은 로그를 기록하다 롤백되었다고 가정하자.
로그 | 설명 |
---|---|
<> | |
<> | |
<> | Rollback 요청 시점에서 마지막 Log |
이 경우 간단히 의 로그레코드를 역순으로 훑으며 변경된 데이터를 원본으로 다시 바꾸는 것으로 충분할 것이다.
이 때 변경된 블록을 다시 원본으로 바꾸는 것은 당연한 얘기이다. 한편 여기서 만약 <>를 복원할 경우 Compensation log record라고 하여 원본레코드에 대응되는 <>를 레코드에 추가하게 된다.
사실 이 부분이 조금 의아할 수 있는데 rollback된 데이터 변경사항을 아예 없던 일로 취급하는 것이 아니라 했다가 취소했다고 명확히 하기 때문이다.
CLR를 쓰는 이유
굳이 굳이 CLR을 쓰는 이유에 대해서 좀 고찰해보자.
먼저 첫째로 들 수 있는 의문점은 왜 기존 레코드 <>를 지우는 것이 아니라 CLR을 쓰는가이다. 이에 대한 이유로 먼저 기존 레코드를 지우는 것은 곤란하다.
로그레코드는 stable storage에 물리적인 연속으로 기록된다. 따라서 한 레코드를 지우고 나머지 로그를 앞당기거나 미는 것이 곤란하기 때문에 그걸 없는 셈 치는 것 자체도 곤란해진다.
한편 다음 이유로는 멱등성을 들 수 있다. 이번 글에서는 로그레코드가 상태(State) 자체를 기록하는 형태이기 때문에 잘 와닿지 않지만 실제 로그레코드는 여러가지 형태로 존재하며, 그 중에는 상태 변화(Transition)을 기록하는 경우도 있다.
어떤 트랜잭션이 반드시 1번 rollback되라는 보장은 없다. 여러가지 이유로 여러번 rollback 될 수도 있는 것이다. 따라서 어떤 로그가 이미 undo되었음을 확실히 명시해주어야 다음번 rollback에서 반복되었을 때 멱등성이 충분히 깨질 수 있다.
다음 이유로 생각해볼 수 있는 것은.. 항상 원하는 시점에 즉시 디스크나 버퍼에 작성할 수 없다는 점일 것이다.
Log와 Buffer block, Physical block은 DBMS 구현상에서 분리되어 일종의 계층적으로 구성된 개념에 가깝다. Log를 읽고 쓸 수 있는 계층에서 직접적으로 Buffer Block이나 Physical block에 쓰는 시점을 조절할 수가 없다. 따라서 Log에 보상레코드를 기록하는 것은 계층적인 설계상 필연적인 일인 것이다.
이제 다시 본론으로 돌아가보면..
따라서 rollback이 이루어진 이후 에 대한 레코드는 다음과 같아진다.
로그 | 설명 |
---|---|
<> | |
<> | |
<> | |
<> | <>에 대한 CLR 추가 |
<> | <>에 대한 CLR 추가 |
<> | 트랜잭션 종료(Complete) |
장애로 인한 재시작
이번엔 시스템 충돌로 인해 DBMS가 의도치 않게 종료된 이후 다시 재시작하며 복원하는 경우이다.
이 경우 먼저 로그레코드를 순차적으로 진행하며 트랜잭션의 상태에 관계 없이 Redo Operation을 수행하고 역순으로 진행하며 아직 완료되지 않은 작업을 모두 롤백한다.
Redo Phase는 다음과 같이 구성된다.
- 종료 시점까지 기록된 로그레코드를 훑으며 가장 마지막 <>레코드를 찾고, 을 undo-list로 설정한다.
- <>로부터 순차적으로 로그를 진행한다.
- 만약 <> 혹은 <>와 같은 레코드를 만나면 모두 redo 작업을 수행한다.
- 만약 <>를 만나면 undo-list에 추가(append)한다.
- 만약 <> 혹은 <>를 만나면 undo-list에서 제거한다.
Undo Phase는 다음과 같이 구성된다.
Undo Phase는 EoL(End of Log)로부터 역순으로 로그를 진행한다. 또한 Undo Phase에서 다루는 모든 트랜잭션 는 반드시 undo list에 존재해야 한다.
- 만약 <> 형태의 로그를 만날 경우 트랜잭션 롤백에서와 같이 <> 로그를 추가하고 에 를 출력한다.
- 만약 <>를 만날 경우 <>를 추가하고 를 undo list에서 제거한다.
- 1.와 2.를 undo-list의 모든 트랜잭션이 제거될 때까지 반복한다.
Undo Phase가 종료되면 다시 DBMS의 정상적인 트랜잭션 동작이 수행가능하다.
좀 더 구체적인 예제를 살펴보자. 다음과 같이 트랜잭션이 진행되다 시스템 충돌이 발생한 것이다.
로그 | 설명 |
---|---|
<> | |
<> | |
<> | |
<> | 마지막 체크포인트. 실행 중이던 트랜잭션 , 이 기록된다. |
<> | |
<> | |
<> | |
<> | |
<> | |
<> | Crash 발생 시점에서 마지막 로그(EoL) |
이 때 시스템 재시작 이후 복원은 어떻게 수행하게 될까?
먼저 Redo Phase를 수행해야 한다.
Redo Phase는 우선 마지막 체크포인트를 찾은 이후 거기서부터 순서대로 진행하며,
Commit과 Abort 여부에 상관 없이 모든 변경 로그에 대해 redo를 수행한다.
로그 | Undo-list | 설명 |
---|---|---|
<> | ||
<> | ||
<> | ||
<> | {} | 가장 마지막으로 발생한 checkpoint를 찾아 undo list를 설정한다. |
<> | redo | |
<> | {} | 에 대한 Commit 로그를 만났으므로 undo list에서 제거한다. |
<> | {} | 에 대한 Start 로그를 만나 undo list에 추가한다. |
<> | redo | |
<> | redo | |
<> | {} | 에 대한 Abort 로그를 만나 undo list에서 제거한다. |
이제 Undo Phase를 수행한다.
Undo Phase는 로그를 역순으로 진행하며 롤백작업을 수행하는 과정이다.
로그 | Undo-list | 설명 |
---|---|---|
... | ||
<> | {} | <>를 만났기 때문에 undo list에서 제거하고 <> |
<> | ||
<> | undo | |
<> | {} | 여기서부터 진행한다. |
따라서 모든 복구작업이 끝난 로그는 아래와 같다.
로그 | 설명 |
---|---|
<> | |
<> | |
<> | |
<> | 마지막 체크포인트. 실행 중이던 트랜잭션 , 이 기록된다. |
<> | |
<> | |
<> | |
<> | |
<> | |
<> | Crash 발생 시점에서 마지막 로그(EoL) |
<> | 롤백 CLR |
<> | 재시작 이후 추가된 레코드 |
Redo Phase에 대한 의문점
어째서 중단된 작업인 의 작업도 전부 redo 연산을 하는 걸까?
이는 undo 작업을 단순화하기 위해서이다. 시스템이 중단되기 전 상황을 그대로 재현함으로써
Undo Phase의 모든 작업은 트랜잭션을 롤백하는 과정과 똑같아진다.
만약 필요 없는 redo 작업을 생략한다면, undo 작업에서 트랜잭션을 롤백할 때 트랜잭션이 어떤 상태에 놓여져있었는지에 따라
처리를 달리해야한다.
따라서 모든 작업을 일단 redo하여 상황을 그대로 재현하고 거기서 순차적으로 여러 트랜잭션을 그대로 롤백하는 것이다.
이러한 기법을 repeating history라고 부른다.
미디어 손실로부터의 복구
마지막으로 미디어 손실 상황에서 어떻게 시스템을 복구하는지 살펴봐야 한다.
이를 위해 몇 가지 추가 개념을 더 살펴봐야 한다.
Log Record Buffering
테이블레코드가 그러하듯 로그레코드도 stable storage에 저장되기 전에 메인메모리에서 잠시 buffering된다. 이는 log record로 인한 IO 비용을 줄이기 위함으로 보통 여러 개의 로그레코드를 모아 한꺼번에 storage로 출력한다.
로그레코드는 기본적으로 메인메모리의 로그레코드 버퍼가 꽉 찼을(full) 경우 or log_force 연산이 실행될 때 stable storage로 출력된다.
log_force 연산은 보통 트랜잭션의 Commit이나 Abort를 stable storage로 출력하기 위해서 사용된다.
Log record의 buffering과 출력은 다음과 같은 규칙으로 수행된다.
- Log record가 stable storage로 출력되는 순서는 해당 레코드가 생성된 순서와 동일하다.
- Transaction enters the commit state <> has been output to stable storage
- 메인메모리의 data block이 디스크로 출력(output)되기 전에 먼저 모든 관련 로그레코드가 stable storage로 출력되어야 한다.
3.의 규칙을 write-ahead logging(WAL)이라고 부른다. undo를 위한 정보가 반드시 먼저 기록되어있어야 한다는 말이다.
Database Buffering
DBMS는 In-memory buffer(Buffer block)와 disk block(Physical block)을 관리한다.
만약 새로운 buffer에 대한 요청이 들어왔을 때, 이미 buffer가 꽉 차있는 경우 기존 buffer block을 제거하고 새로운 buffer block을 생성한다.
이 때 제거 대상이 된 buffer block이 만약 변경정보를 가지고 있을 경우(dirty block) 이를 먼저 디스크에 출력해야 한다.
만약 데이터 블록이 디스크로 출력 중일 경우 해당 데이터에 대한 변경작업이 수행되지 않는다. 이는 exclusive lock을 통해 수행되며, 쓰기작업이 완료되면 lock을 release하는 방식으로 수행한다.
만약 Buffer Block을 디스크로 출력할 경우
- Acquire exclusive lock on the block - 쓰기 작업을 수행하는 동안 모든 변경작업으로 인한 에러를 방지
- 관련 로그를 모두 stable storage로 출력(flush)한다.
- Buffer block을 디스크로 output한다.
- release exclusive lock on the block
Buffer Management
DBMS buffer의 실제 구현은 어떻게 이루어지는가?
보통 두 가지 방식 중 하나인데, 1. 메인메모리의 일정 부분을 데이터베이스가 미리 예약하여 사용하거나, 2. 가상메모리를 이용한다.
**메인메모리를 미리 예약(reserve)**하는 경우...
- 데이터베이스를 위해 할당할 수 있는 메모리가 제한
- DBMS를 위한 메모리 영역으로 인해 다른 응용프로세스를 위한 메모리가 제한
만약 가상메모리를 이용할 경우...
- Dual paging problem
- 만약 운영체제에서 변경된 페이지를 evict할 경우 페이지는 디스크의 swap space에 출력된다.
- 데이터베이스가 swap space에 있는 buffer를 디스크로 출력하고자 할 경우 buffer를 swap space로부터 읽어 데이터베이스로 출력해야 한다.
- 즉, 읽고(input) 써야(output)하는 추가적 IO 비용이 발생
- (이상적인 경우) 운영체제가 evict하고자 하는 데이터 블록의 제어권을 먼저 데이터베이스에 제공하면 dual paging을 피할 수 있다.
- 만약 변경된 buffer일 경우 swap space로 paging하는 대신 먼저 데이터베이스로 출력하고 해당 buffer block을 release하면 된다.
- 그러나 실제 운영체제는 보통 이러한 기능(제어권)을 제공하지 않는다.
Fuzzy Checkpointing
Checkpointing을 살펴보면 생각보다 긴 절차가 걸릴 수 있음을 알 수 있다. 또한 Checkpoint 연산을 수행하는 동안은 데이터베이스의 상태가 동결되기 때문에 모든 변경작업이 중단되어야만 한다.
이를 해소하기 위해 checkpointing을 수행하는 동안 업데이트가 가능하도록 하는 것이 Fuzzy checkpointing이다.
Fuzzy checkpointing은 다음과 같이 수행된다.
- 일시적으로 트랜잭션에 의한 모든 변경을 중단한다.
- <> 로그를 작성하고 모든 로그를 stable storage로 출력한다.
- 변경된 모든 buffer block의 list 을 따로 저장한다.
- 이제 모든 트랜잭션 변경작업을 허용한다.
- 의 모든 buffer block을 디스크로 출력(output)한다.
- 이 때 모든 block은 변경이 제한(lock)되고, WAL 규칙을 준수해야 한다.
- last_checkpoint 포인터를 통해 checkpoint 레코드의 주소를 기록한다.
기존 체크포인팅과 비교하면 변경된 buffer block을 disk로 먼저 출력하는지 여부가 달라지게 된다. 이 때 만약 체크포인팅을 수행하는 동안 시스템이 중단되면 어떻게 할까?
이를 위해 마지막에 last_checkpoint 포인터를 이용하게 된다.
재시작 시점에서 참고할 체크포인트를 마지막으로부터 거슬러올라가는 것이 아니라, last_checkpoint에 저장된 체크포인트로 참고하기 때문에 체크포인팅을 하는 동안 시스템 장애로 중단되더라도 재시작 시점에서는 올바른(완료된 체크포인트) 로그레코드를 참고하게 된다.
Dump
Nonvolatile storage의 손실로부터 데이터를 복구하기 위해선?
storage 전체를 stable storage로 백업해야 한다.
이를 위해 주기적으로 수행하는 것이 dump 작업이다.
dump는 checkpointing과 유사한 목적인데 일관성이 보장된 데이터베이스의 상태를 그대로 복원하는 것이 목적이다.
보통 데이터베이스는 주기적으로 dump 작업을 수행하며 dump 작업을 수행하는 동안은 모든 트랜잭션이 비활성화된다.
Dump 작업은 checkpointing과 유사하게 수행된다.
- 메인메모리의 모든 log record를 stable storage로 출력한다.
- 메인메모리의 모든 buffer block을 디스크로 출력한다.
- 디스크의 데이터베이스 콘텐츠를 stable storage로 복사한다.
- <> log를 stable storage로 출력한다.
이제 정말 미디어 손실로부터의 복구를 다루어보자.
시스템 충돌로부터 재시작한 경우와 마찬가지로 미디어 손실 복구도
먼저 가장 최근의 dump를 데이터베이스로 복원한 뒤, dump 이후로 commit된 모든 트랜잭션을 redo하는 방식으로 실행된다.
dump 또한 마찬가지로 많은 시간이 소요될 수 있는데, checkpoint와 비슷하게 fuzzy dump 혹은 online dump를 지원하여 빠르게 트랜잭션 실행을 보장하는 경우가 있다.