Published on

로그 기반 복구 작업-2

Intro

이전 글에 이어 더 심화된 논의를 진행해보자.
주로 이전 글에서 다룬 회복 기법에서 성능을 높이기 위한 다양한 테크닉을 다룰 것이다.


Remote Backup System

원격 백업 시스템은 말그대로 물리적으로 떨어진 백업 시스템을 구성하여 원본 시스템이 중단되더라도 백업시스템이 그 역할을 빠르게 대체하여 트랜잭션이 수행가능한 시스템이다.

원격 백업 시스템의 주 목적은 고가용성(high availibility)의 확보이다. 즉, 전체 시스템이 중단되는 시간을 최대한 줄이고 빠르게 정상 작동하도록 하는 것이다.

Detection of Failure

백업 시스템은 물론 원본 시스템이 제대로 동작하지 않는 것을 감지할 수 있어야 할 것이다.

이는 주로 Heart-beat message(주기적인 생존 신호.. 이름이 매우 직관적이다.)를 이용한다. 이 때 시스템 간의 링크를 다중화하는데, 이는 원본 시스템의 failure와 링크의 failure를 오해할 수 있기 때문이다.

Transfer of Control

만약 원본 시스템이 붕괴되어 백업 시스템이 그 역할을 수행해야 한다고 하자. 그럼 백업 시스템은 사전에 dump된 데이터를 통해 먼저 데이터베이스를 복원하고 원본 시스템으로부터 받았던 log record를 복원하여 그 자신이 새로운(new) 원본 시스템이 된다.

만약 다시 원본 시스템(old primary site)이 복구되어 전체 시스템의 제어권을 회복하고자 한다면 백업시스템(현재는 원본 시스템으로 작동 중인)으로부터 모든 로그레코드를 수신하고 변경된 사항을 다시 반영해야 한다.

Time to recover

서비스 중단과 회복 사이의 간격(delay)를 줄이기 위해 백업 시스템은 주기적으로 redo log record를 내부적으로 처리하고, checkpointing을 수행하여 회복 상황에서 필요한 log record의 크기를 줄인다.

매우 빠른 서비스 회복을 위해 redo record를 전송받는 즉시 백업 시스템 내부적으로 데이터베이스 상태에 반영하는 경우도 있다.

Durability

Remote Backup system에서 문제가 될 수 있는 요소는 바로 트랜잭션이 durability이다.
완전한 durability를 보장할 경우 트랜잭션의 주기가 길어질 수 있기 때문에 낮은 durability를 허용할 수도 있다.

설명
One-safe원본 시스템(primary site)에서 커밋 레코드가 작성되는 즉시 트랜잭션 커밋
Two-very-safe트랜잭션의 커밋 레코드가 원본과 백업에 모두 기록될 때까지 커밋을 대기. 만약 어느 한쪽이라도 시스템이 중단될 경우 커밋을 할 수 없기 때문에 가용성이 떨어진다.
Two-safe만약 원본-백업 시스템이 모두 작동 중일 경우 two-very-safe와 동일하다. 대신 원본 시스템만 동작 중일 경우 one-safe처럼 작동하여 one-safe와 two-safe 사이에서 적당선을 취한다.

Recovery with Early Lock Release and Logical Undo Operations

이번엔 early lock release와 logical undo를 통해 높은 동시성을 확보하는 회복 기법에 대해 살펴볼 것이다.

Early Lock Release

Early lock release의 기본 골자는 트랜잭션이 실행되는 동안 더 이상 필요 없는 락은 트랜잭션이 커밋되기 전에 일찍 해제한다는 것이다.

이를 통해 다른 트랜잭션이 더 빨리 공유데이터에 접근할 수 있기 때문에 동시성을 더 높일 수 있을 것이다.

그런데 이러한 작업에서 트랜잭션의 ACID를 기존 방식대로 보장할 수 있을까?

Logical Undo Logging

이전의 글에서 얘기했던 내용에 이어진다.

Early lock release를 한다면, 아직 트랜잭션이 끝나지 않아도 다른 트랜잭션이 데이터 상태를 변경할 수 있다는 말이다. 따라서 Physical state를 기록하는 방식으로 undo를 진행하면 다른 트랜잭션이 변경한 정보가 소실될 수 밖에 없다.

이를 위해 데이터의 상태가 아닌 상태변경의 정보를 저장한다는 것이 핵심이다.

이러한 Logical undo를 이용할 수 있는 대표 사례로 B+-tree가 있다. B+-tree는 인덱스 데이터를 저장하기 때문에 필연적으로 동시에 여러 트랜잭션이 필요로 할 가능성이 높다. 이 경우 다른 트랜잭션이 B+-tree를 변경하면 physical state만으로 tree를 복구하기 어려워진다.
따라서 insertion과 deletion이 서로 역연산임을 이용해 logical undo log를 작성하는 것이다.

비슷한 사례로 만약 여러개의 data tuple을 삽입(삭제)할 경우 일정 영역의 공간에 lock이 필요한데, 이 때도 비슷하게 이용할 수 있을 것이다.

만약 은행 계좌로부터 금액을 인출하는 연산을 할 경우도 계좌 잔액에 여러 트랜잭션이 접근할 수 있기 때문에 이 기법을 이용할 수 있다.

Physical Redo

Redo 작업의 경우 여전히 물리적으로 상태자체를 기록한다.
이유는 나도 좀 헷갈리긴 하지만 내가 정리한 내용은 다음과 같다.

  • 우선 Logical redo 자체가 좀 곤란한 측면이 있다.
    redo가 필요한 상황은 Transaction rollback(normal operation)이 아니라 시스템 충돌이나 미디어 복구로부터의 시스템 재시작의 경우이다.
    통상적인 rollback의 경우 DBMS에 의해 디스크가 지속 관리되고 있는 상황에서 벌어지는 상황이지만, 시스템이 재시작하는 경우 logical redo를 통해 이전 시점을 완벽하게 재현할 수 있다는 보장이 안된다. 이미 처리된 연산을 재수행할 수도 있는 것이며, 각 physical block 자체가 깨진 상태일 수도 있다.
    즉, 재시작한 상태의 데이터베이스가 logical operation으로부터 일관성이 유지될 수 있는 상태라는 것이 보장되지 않는다.

  • Physical redo log를 기록해도 early lock release를 사용하는 것에 문제가 없다. 이는 후술하는 구체적 사례를 살펴보면 자연스레 이해된다.

Operation Logging

Operation Logging은 다음과 같이 수행된다.

  1. Operation이 시작하면, <Ti,Oj,operationbeginT_i, O_j, operation-begin> 로그를 기록한다. 이 때 OjO_j는 operation instance에 대한 unique identifier
  2. Operation을 수행하는 동안, 이전과 같이 physical redo와 physical undo log를 기록한다.
  3. Operation이 완료되면, <Ti,Oj,operationend,UT_i, O_j, operation-end, U> 로그를 기록한다. 이 때 UU는 logical undo를 수행하기 위한 정보가 담겨있다.

예컨데 (key, record-id) pair로 이루어진 데이터 (K5, RID7)을 index I9에 삽입한다고 가정해보자. 이 때 Key는 X(물리적 주소)에 위치하고, record-id는 X+8에 위치한다.

그러면 다음과 같은 로그를 작성하게 된다.

로그
<T1,O1,operationbeginT_1, O_1, operation-begin>
<T1,X,OLD1,K5T_1, X, OLD1, K5>
<T1,X+8,OLD2,RID7T_1, X+8, OLD2, RID7>
<T1,O1,operationend,(deleteI9,K5,RID7)T_1, O_1, operation-end, (delete I9, K5, RID7)>
  • 만약 operation이 완료되기 전에 crash/rollback이 발생할 경우,
    operation-end log가 아직 기록되지 않았으므로 physical undo 정보를 이용해 undo를 수행한다.

  • 만약 operation이 완료되고 crash/rollback이 발생할 경우,
    operation-end log가 기록되었으므로 logical undo 정보(UU)를 이용해 undo를 수행하고 physical undo log는 무시한다.

  • Crash 이후 Redo 작업은 똑같이 physical redo log를 이용한다.

Transaction Rollback with Logical Undo

트랜잭션 TiT_i가 롤백될 경우 TiT_i의 로그를 역순으로 진행하며 다음과 같이 undo 작업을 수행한다.

  1. 만약 <Ti,Xj,V1,V2T_i, X_j, V1, V2> 로그를 만날 경우, physical undo를 수행하고 <Ti,Xj,V1T_i, X_j, V1>를 기록한다.

  2. 만약 <Ti,Ok,operationend,UT_i, O_k, operation-end, U> 로그를 만날 경우,

  • UU를 이용하여 logical undo를 수행한다.
  • 이 때 rollback을 통해 발생하는 변경작업은 모두 normal operation과 똑같은 형식(<Ti,Xj,V1,V2T_i, X_j, V1, V2>)으로 기록된다.
  • operation이 모두 rollback될 경우 operation-end log 대신 <Ti,Ok,operationabortT_i, O_k, operation-abort>를 기록한다.
  • <Ti,Ok,operationbeginT_i, O_k, operation-begin>을 만날 때까지 모든 로그를 무시한다.
  1. redo-only record(CLR 등)를 만날 경우 무시한다.

  2. 만약 <Ti,Ok,operationabortT_i, O_k, operation-abort> 로그를 만날 경우, <Ti,Ok,operationbeginT_i, O_k, operation-begin> 로그를 만날 때까지 모든 로그를 무시한다.

  3. <Ti,StartT_i, Start>를 만날 경우 <Ti,AbortT_i, Abort>를 기록하고 롤백을 종료한다.

4.는 중복된 롤백 연산을 방지하기 위함을 쉽게 이해할 수 있을 것이다.
또한 3.과 4. 케이스는 트랜잭션이 롤백되는 도중에 데이터베이스 crash가 발생하는 경우에만 발생한다는 점도 체크해야 한다.

구체적인 시나리오를 통해 살펴보도록 하자.
다음과 같이 T0T_0가 진행하여 두 개의 operation을 진행하다가 rollback된 경우를 생각한다.

로그설명
<T0,StartT_0, Start>
<T0,B,2000,2050T_0, B, 2000, 2050>
<T0,O1,operationbeginT_0, O_1, operation-begin>
<T0,C,700,600T_0, C, 700, 600>만약 T0T_0O1O_1이 종료되기 전에 취소될 경우 physical undo를 위해 사용할 정보이다.
<T0,O1,operationend,(C,+100)T_0, O_1, operation-end, (C, +100)>T0T_0O1O_1을 완료했기 때문에 CC에 대한 lock이 release된다.
<T1,StartT_1, Start>
<T0,O2,operationbeginT_0, O_2, operation-begin>
<T1,O3,operationbeginT_1, O_3, operation-begin>
<T1,C,600,400T_1, C, 600, 400>T0T_0CC에 대한 lock을 release했기 때문에 T1T_1CC에 대한 lock을 걸고 수정 가능하다.
<T1,O3,operationendT_1, O_3, operation-end>T1T_1CC에 대한 lock을 release한다.
여기서 T0T_0가 일어났다고 가정하자.
<T0,C,400,500T_0, C, 400, 500>T0T_0에 대한 로그를 역순으로 진행하며 먼저 O1O_1에 대한 logical undo를 진행한다.
<T0,O1,operationabortT_0, O_1, operation-abort>O1O_1에 대한 undo가 완료
<T0,B,2000T_0, B, 2000>physical undo log에 대해선 똑같이 rollback한다.
<T0,AbortT_0, Abort>
<T1,CommitT_1, Commit>

Recovery Algorithm for Logical Undo

약간 수정된 Transaction Rollback을 다루었으니 이제 Recovery algorithm도 다루어보자. 이번에도 약간 다르다.

Logical undo를 이용한 recovery 작업은 다음과 같이 이루어진다.

  1. Redo Phase
    Redo Phase는 마지막 checkpoint로부터 순서대로 로그를 진행하며 이루어진다.
  • Repeating history by physically redoing all updates of all transactions
  • undo-list를 생성하는 규칙은 다음과 같다.
    • 초기에는 LL로 리스트를 설정.
    • 만약 <Ti,StartT_i, Start>를 만나면 undo-list에 추가
    • 만약 <Ti,CommitT_i, Commit> 혹은 <Ti,AbortT_i, Abort>를 만나면 TiT_i를 undo-list에서 제거

이제 undo list에는 아직 완료되지 않은 트랜잭션만이 남게 된다.

  1. Undo Phase
    Undo Phase는 마찬가지로 로그를 역순으로 진행하며, 그 대상은 undo-list에 포함된 트랜잭션이 된다.
  • 모든 트랜잭션을 롤백하고 필요할 경우 추가 로그를 작성한다.
  • 마찬가지로 undo-list가 비워지면 종료한다.