복구 : 장애 이전의 일관된 상태로 복원하는 과정
- 트랜잭션 장애 : 트랜잭션 내의 논리적 오류나 잘못된 입력 데이터, 또는 시스템 내의 자원부족 등으로 인해 트랜잭션 중단될 때 발생
- 시스템 장애 : 정전이나 핟드웨어 결함으로 인해 작동 중단되는 경우. 주기억장치와 같은 휘발성 저장장치의 내용이 분실/손상 된다
- 미디어 장애 ; 디스크와 같은 비휘발성 저장장치의 일부/전체가 손상된 상태
데이터베이스 시스템이 장애로부터 ‘복구’되는 기본 원리 = 데이터의 ‘중복’
- 백업 : 미디어 장애 대비해 주기적으로 데이터베이스의 내용을 DVD나 자기테이프 같은 저장장치에 백업 파일 만들어 보관. 디스크에 장애 발생하면 이 정보 이용해 데이터베이스를 가장 최근에 저장한 백업 파일로 복구 가능
- 복제 (mirroring) : 하나의 데이터베이스를 서로 다른 디스크에 복제(미러링)해 저장하는 것. 하나의 디스크가 파괴됐을 때 다른 디스크를 이용해 데이터베이스를 계속 운용할 수 있음 (=은행 등의 데이터 민감한 곳에서 씀. 속도 빠르지만, 경제적 부담 있음)
트랜잭션 장애/시스템 장애의 경우
if) A계좌에서 출금 된 상태에서 정전으로 인해 B계좌에 입금되지 못하고 시스템 정지. 시스템이 재가동됐을 때 이 트랜잭션은 완료되지 못한 상태이므로 트랜잭션의 원자성 특성 만족시키기 위해 A계좌에서 출금된 내용이 원래대로 복귀돼야함
=> 디스크 같은 ‘비휘발성 저장장치’에 트랜잭션이 장애 발생하기 전에 데이터베이스에 어떤 순서로 갱신 이뤄졌는가를 나타내는 정보 기록돼있어야 복구 가능
=> 이 기록이 ‘로그’
데이터베이스는 비휘발성 저장 장치인 디스크에 상주하고, 블록 이라는 고정 크기단위로 분할돼있다.
주기억장치(휘발성)-디스크 사이의 입출력 작업은 블록 단위로 처리됨.
데이터베이스 시스템에서는 주기억장치에 버퍼를 관리함(디스크의 입출력 작업 최소화하고 성능 극대화 위해) => 데이터베이스가 디스크로부터 특정 데이터 항목에 대해 읽기 연산 수행하기 위해선 해당 데이터 항목이 저장된 블록을 주기억장치의 버퍼를 읽어들임. 읽고자 하는 블록이 이미 버퍼에 존재하면 해당 블록은 디스크에서 다시 읽을 필요 없고 버퍼에서 읽으면 됨.
쓰기 연산도 디스크의 블록에 직접 쓰는 연산 수행하지 않고 해당 블록을 우선 버퍼로 읽어들인 후 버퍼에서 쓰기 연산 수행. BUT, 이 때 버퍼에 저장된 블록 디스크에 바로 저장할 필요는 없음. 이 블록에 대한 다른 읽기/쓰기 연산이 필요한 경우, 이 연산들을 버퍼에서 모두 수행한 후에 나중에 한 번에 디스크로 저장하면 디스크에 대한 입출력 연산 최소화 할 수 있기 때문! 따라서, 갱신된 블록들은 이후 적당한 시긴에 디스크로 저장됨 (예-주기억장치의 버퍼가 다 차서 새로운 블록 읽을 수 없을 때, 버퍼에 저장된 블록들은 버퍼에서 삭제되고 갱신된 내용들은 디스크에 저장돼야함)
로그 : 디스크에 저장되고, 트랜잭션들의 모든 갱신 활동 기록
=> 트랜잭션 실행 도중 장애 발생할 경우 로그 통해 트랜잭션 실행 이전의 상태로 복귀 가능하고 이미 완료된 트랜잭션에 대한 갱신 기록들 알아낼 수 있음
<T start> - 트랜잭션의 시작
<T commit> - 트랜잭션의 완료
<T abort> - 트랜잭션의 중단
<T, x, v1, v2> - 데이터 항목에 대한 갱신 (트랜잭션 T가 데이터 항목 x에 대해 v1을 v2로 갱신함)
=> 트랜잭션이 중단돼 복귀할 경우 v1의 정보를 이용해 원래 값으로 돌려놓을 수 있고, 완료된 트랜잭션에 대한 갱신 기록은 v2를 이용해 알아낼 수 있다
● 로그 우선기록 규약 : 로그 레코드를 기록할 때 가장 중요한 건 트랜잭션이 갱신한 데이터 항목을 데이터베이스에 기록하기 전에 로그 레코드에 우선 기록해야 한다는 것. 트랜잭션이 실행 도중 장애로 인해 실패했을 때 데이터베이스에 이미 기록된 변경 내용을 취소하려면 로그 레코드에 그 기록이 남아있어야 하니까. BUT, 만약 데이터베이스에 갱신된 결과가 먼저 기록되고 그 사실을 로그 레코드에 기록하기 직전 장애가 발생하면 로그에 갱신된 기록이 남아있지 않아 데이터베이스의 내용 원래대로 복귀시키지 X
=> 이미 완료된 트랜잭션에 대해 데이터베이스의 갱신 내용을 디스크에 반영할 때도 유용! 예로, 트랜잭션이 성공적을 완료되면 로그 레코드는 그 동안의 갱신 기록들에 대한 레코드가 우선 기록됨. BUT, 실제 디스크엔 데이터베이스의 갱신 기록이 반영 되지 않고 주기억장치의 버퍼에만 남아있을 수 있음. 만약 이 때, 정전으로 인해 시스템이 정지되면 버퍼에 저장된 트랜잭션의 갱신 내용은 사라짐. BUT, 시스템이 재가동됐을 때 로그엔 이미 완료된 트랜잭션의 갱신 기록이 남아있으므로 이 정보 이용해 실제 데이터베이스의 변경 내용 반영시킬 수 있음.
로그 이용한 복구 기법
UNDO 연산 : 트랜잭션 실패했을 때 연산. 갱신된 값을 그 이전의 값으로 되돌려 놓음
REDO 연산 : 성공됐을 때 실행하는 연산(갱신된 값 재실행). 쓰기 연산을 실행했지만 실제로 디스크로 반영 안 됐을 경우 재실행하는 연산임.
트랜잭션 장애/시스템 장애가 발생했을 때 로그를 이용하는 방법에는 디스크에 데이터베이스의 갱신 내용을 저장하는 시점에 따라 ‘지연 갱신’을 기반으로 한 복구 기법과 ‘즉시 갱신’을 기반으로 한 복구 기법으로 나눌 수 있음.
- 지연 갱신을 기반으로 하는 복구 기법 : 지연 갱신은 트랜잭션이 실행이 성공적으로 완료될 때 까지 갱신 내용을 디스크에 저장하지 않고 지연 시키는 방법. 따라서, 트랜잭션이 실행되는 동안엔 갱신된 내용이 주기억장치의 버퍼에 기록되고, 트랜잭션이 완료한 후의 적당한 시점에 버퍼의 내용이 디스크로 저장됨.
● 트랜잭션이 완료됐다 = 디스크에 저장된 로그에 <T commit>가 기록됐다. 트랜잭션 T가 완료되기 전에 장애가 발생해 복구할 경우엔 디스크에 갱신된 내용이 저장되지 않았으므로 이전 값으로 복구할 필요가 없기 때문에 UNDO 필요없음. BUT, T가 완료됐을 경우엔 그 내용이 디스크에 저장되지 않았을 가능성 있으므로 REDO 연산 필요. 따라서, 로그 레코드에 저장될 갱신기록에서 이전 값은 저장할 필요 없고 갱신 이후 값만 저장하면 됨. (완료되지 않았으면 어차피 디스크에 저장x – redo 연산 필요 없음)
=> <T, x1, x2>
● 지연갱신 기반 복구 알고리즘 : UNDO 연산 필요 없으므로 NO-UNDO/REDO 알고리즘이라 함. 로그 파일의 ‘처음부터’ 기록을 순차적으로 검색하고 로그에 저장된 각 트랜잭션의 T의 로그 레코드에 대해 <T commit>가 저장됐으면 이 트랜잭션의 갱신 기록에 대해 REDO 연산 수행. <T commit> 나머지 트랜잭션에 대한 로그 레코드들은 무시. (완료되지 않은 것들은 어차피 디스크에 저장돼있지 않아서)
- 즉시 갱신을 기반으로 하는 복구 기법 : 지연 갱신과 달리 트랜잭션의 수행 도중 언제든 데이터의 갱신 내용을 디스크에 저장 가능. 지연 갱신과 마찬가지로 버퍼의 내용이 디스크에 저장되기 전 반드시 그에 대한 로그 기록 먼저 저장돼야함. 즉시 갱신에서도 트랜잭션이 완료됐다는 것은 로그에 <T commit>가 기록됐다는 것으로 판단. BUT, 트랜잭션 실행 도중에 데이터베이스에 대한 갱신 가능하므로, 장애가 발생해 재가동 되면 완료 못한 트랜잭션에 대해서는 원래의 값으로 돌려놓는 UNDO 연산 필요! 또한, 완료된 트랜잭션에 대해서도 디스크의 반영 사실 확신할 수 없으므로 REDO 연산 필요.
=> 갱신에 관한 로그 레코드에 이전 값과 이후 값 모두 저장돼야함
● 즉시 갱신 기반 복구 과정 : 로그 파일이 기록된 ‘마지막부터 반대방향’으로 순차적으로 검색하고, 로그에 저장된 각 트랜잭션 T의 로그 레코드에 대해 <T start>가 있으나 <T commit>가 없으면 이 트랜잭션의 갱신 기록에 대해 UNDO 연산 수행(실패했으니 본래값으로 돌려놔야함). 마지막부터 거꾸로 올라가 로그 파일의 처음에 도달했으면 반대 방향으로 순차적으로 검색. 로그에 저장된 각 트랜잭션의 로그 레코드에 대해 <T commit>가 저장됐으면 이 트랜잭션의 갱신 기록에 대해 REDO 연산 수행
● 즉시 갱신 기반 복구 알고리즘 : UNDO/REDO 알고리즘. 기록된 순서의 반대 방향으로 UNDO 연산 먼저 실행해 완료 못한 트랜잭션들이 갱신한 모든 기록 원래 상태로 복귀시키고 완료된 트랜잭션들에 대해 갱신 내용들 디스크에 저장하는 REDO 연산 실행함. (UNDO/REDO 있을 땐 무조건 UNDO 먼저!)
검사점 이용한 복구
: 로그는 트랜잭션이 실행되며 계속 축적되므로, 로그들이 삭제되지 않고 축적만 되면 그 양은 방대해짐. 이 때 시스템에 장애가 발생해 로그 이용한 복구 할 경우 전체 로그를 모두 검색해야 하는 부담이 생김. 또한, 완료된 지 오래된 트랜잭션들은 대부분 디스크에 그 내용이 저장돼있으므로 시스템 장애에 의해 복구를 할 시점엔 REDO 같은 연산필요 없음
=> 데이터베이스 운용 과정에서 주기적으로 버퍼의 모든 갱신 기록 디스크에 저장하면, 복구 실행할 때 모든 로그를 검색하는 게 아니라 마지막으로 저장된 시점 이후의 로그 기록만을 검색하면 된다.
이처럼 주기적으로 디스크에 갱신된 내용을 모두 디스크에 기록하는 시점 = ‘검사점’ (checkpoint)
검사점은 현재까지 로그에 기록된 내용과 디스크에 저장된 데이터베이스의 내용을 일치시키는 과정! 따라서, 검사점 이후에 실행 과정에서 장애 발생하면 검사점 이전에 완료된 트랜잭션에 대해선 UNDO/REDO 연산 필요 없음. 검사점 이후에 완료된 트랜잭션 또는 장애 시점까지 아직 완료되지 않은 트랜잭션에 대해서만 UNDO/REDO 연산 실행하면 됨.
복구 과정 => 장애 발생 시점~검사점까지 역순으로 진행하며 완료되지 않은 트랜잭션에 대해 UNDO 실행하고, 검사점 이후 완료된 트랜잭션에 대해선 반대 방향으로 REDO 실행
*검사점 이전에 완료된 트랜잭션에 대해선 복구할 필요 없음.