# 실패(장애)의 종류
1. 트랜잭션 실패
1) 논리적 에러 : 자바 코드에서 에러
– 잘못된 입력이나 데이터 부재, 오버플로우, 자원의 한계 초과 등의 내부 조건
- 더 이상 실행 지속 못함
- ex) 0으로 나눔
2) 시스템 에러
- deadlock 발생(충돌 직렬성 얻기 과정에서 발생)
- 재실행 가능
- 타임스탬프(뷰직렬성 얻기 과정에서 발생)-deadlock보다 빈도 잦음(waiting보다 죽임)
2. 시스템 손상
- 전원 나감. 하드웨어, 소프트웨어 에러. 운영체제 실패.
- 모든 트랜잭션들 장애 발생
- 비휘발성 저장 장치(디스크, 플래시)의 데이터를 그대로 보관 => 실패 중지 가정
- 내부적으로 체크(체크섬)로 데이터 복구
- 디스크 내에 있던 데이터는 손상 없음
3. 디스크 고장
- 디스크 헤드가 디스크 판 닿음(긁음) => 고장 - 평소에는 떠있음(전자기적)
- 다른 디스크에 사본을 만들거나 테이프, DVD와 같은 3차 저장 매체에 백업해서 복구
장애 강도 : 1 < 2 < 3
# 복구 알고리즘 – 장애 발생 후 어떻게 복구할지
- 장치의 장애 유형 확인
- 데이터베이스의 내용에 어떤 영향을 미칠지 고려
- 데이터베이스의 일관성과 트랜잭션의 원자성을 보장하는 알고리즘
ex ) A에서 B로 50달러 송금 => 두 개의 계좌를 update A:A-50, B:B+50
- A:A-50, B:B 이거나 A:A, B:B+50이면 장애 발생
1) 카밋 전에 DB buffer에서 디스크 DB 쪽으로 변경된 값(A:A-50, B:B+50) 가버린 경우에, 카밋 전에 프로그램 장애 발생하면 => A, B 원래 값으로 복구 시킴
2) 카밋하고 DB buffer 내의 값이 디스크의 DB로 일부 안 가있는 경우에 장애 발생하면,
DB buffer의 값은 날라감, 디스크의 DB값은 안 변함 => 복구해도 송금 안 되었다고 함
1. 복구에 필요한 정보를 모으는 과정
- 로그에 존재 (트랜잭션 동작 과정들 기록)
2. 복구하는 과정
- 원자성, 일관성, 지속성을 위함
# 저장 장치
1. 휘발성 저장 매체
- 시스템 손상하면 데이터 날라감
- 메인 메모리, 캐쉬
2. 비휘발성 저장 매체
- 시스템 손상해도 데이터 살아있음
- 비휘발성 RAM, 디스크, 플래시, 테이프
3. 안정 저장 매체
- 이상적인 저장장치(신비로운 장치)
- 절대로 데이터 잃어버리지 않음
- 운영의 차이
- 여러 장비에 복사본 둠
- 로그 파일에 사용 – 데이터 기록물
- 비휘발성 저장장치를 운영하는 일종의 관리 정책
# 데이터 접근 (Data access)
- 블록 : IO 단위. 고정 길이 단위
- 물리적 블록 : 디스크안의 블록
- 버퍼 블록 : DB Buffer안의 블록. 메인 메모리 안
< 명령어 (블록 단위) >
1) input(B) : 물리적 블록을 메인메모리로 읽어옴
2) output(B) : 버퍼 블록을 디스크로 보냄
- Tx : Java A() 라는 프로그램 안에 존재. SQL에 읽어옴
=> 자바코드들이 x값 처리. x1이라는 변수 공간에 x값 가져옴
- DB Buffer안에 원하는 블록 없으면 디스크에서 가져오고 write 하고 다시 디스크로 감
- DB x(항목)와 Pgm x1(변수)을 연결 (바인딩)
- work area of T1 : 오라클에서는 PGA (연결되면 배정됨)
- 휘발/비휘발성 장비와의 제약조건 => 일관성 깨짐
- 카밋해서 값 바꿨으면 디스크 값은 안 바뀌었을 수도 있음 => output(B)해야 바뀜
- 디스크 값 안바뀐 상태에서 장애 발생하면 날아가고 복구 => 카밋된 값 보존 안됨
- 카밋 전에 디스크로 블록 가고 장애 발생해서 복구 => 변경된 값 나옴(잘못된 값)
< 연산 >
- input()만 실행, output() 실행 안함
1) read(X)
- 데이터 항목 X의 값을 지역 변수 xi에 할당
- 1-1) X가 위치하는 블록 Bx가 메인 메모리에 없을 경우 input(Bx) 실행
- 1-2) 버퍼 블록으로부터 X의 값을 xi에 할당
2) write(X) - insert into, delete, update
- DB Buffer의 블록에 대한 작업
- 디스크 상의 블록으로 바로 write 되는 거 아님
- DB Buffer 관리자가 output하면 디스크에 write 됨
- 카밋 전에 write 실행
- write 실행하고 output(Bx) 실행되기 전 장애 발생하면 갱신 내용 잃음
- 지역 변수 xi의 값을 버퍼 블록 안에 있는 데이터 항목인 X에 할당
- 2-1) X가 위치하는 블록 Bx가 메인 메모리에 없을 경우 input(Bx) 실행
- 2-2) 버퍼 Bx 내의 X에 xi값을 할당함
# 복구와 원자성 – all or nothing
- 뭘 읽었는지는 다루지 않음 (read() 한 내용 없음)
- 뭘 고쳤는지 복구 대상 (write() 한 내용만)
1) 로그 기반 복구 – 주로 사용
2) 그림자 페이징 – 거의 사용 안함(사본 생성)
# 로그 기반 복구 (log-based recovery)
- 파일 => 로그 레코드의 모음 (bit -> byte -> field -> record -> file)
- 데이터베이스의 모든 갱신 작업 기록
- 데이터베이스 변경 전, 로그 레코드 생성되고 로그에 추가
- 안정 저장 장치에 있음
- 트리 아님. 한 줄로 시리얼 하게
- update 관련 사항만 유지관리
- Ti 시작할 때 : <Ti start>
- Ti write(X) : <Ti, X, V1, V2> V1:old, V2:new value
- Ti 끝나면 : <Ti commit>, <Ti abort>
# 로그 사용 방법
- update 로그 레코드는 안정 장치로 바로 간다고 가정
- 데이터베이스 수정 : Tx이 디스크 버퍼 또는 디스크 자체를 갱신
1. 지연된 변경 (deferred-modification)
- 디스크 상의 DB
- output()(갱신한 내용) 작업을 Tx이 카밋할 때까지 미룸
- 시스템적으로 부담(work area 크기 키움) : 모든 갱신된 데이터 항목에 대한 사본 유지
2. 즉시 변경 (immediate-modification) : 주로 사용
- 무조건 즉시는 아님
- output()작업을 Tx 카밋 전이라도 디스크 상의 DB로 output 할 수 있음
- 먼저 write 했어도 먼저 output 되는 건 아님 (Ti 순서 != output 순서)
- write 전에 로그 파일에 적음
# 트랜잭션 카밋
- Tx의 마지막 레코드인 카밋 로그 레코드가 안정 저장 장치에 기록된 경우
- 장애가 발생해도 갱신 작업들 다시 수행할 수 있는 충분한 정보 가짐
- 송금이 완료되어졌다는 Tx의 카밋 레코드가 로그 파일에는 확실히 적어 놈
- 디스크 DB에 있다는 게 아님
- 카밋 전의 write 작업들
- 메시지 받으면 송금 했다는 작업 자체는 데이터 날아가도 회복할 수 있음을 보장
- 송금 취소 메시지 받으면 abort 된 것
- DB에 전혀 반영되지 않는 게 아님. 일부 반영할 수도
- 로그 파일에 되돌릴 수는 작업 기록. 취소 반영할 수 있도록 보장
# 동시성 제어와 복구 (Concurrency control and recovery)
- 같은 시간에 여러 Tx 돌아감. SGA(single disk buffer)
- 어떤 Tx이 어떤 데이터 고치면 commit 이나 abort 하기 전에 다른 Tx이 write 하는 건 없음(더티 기록)
- Lock-X를 commit이나 abort 할 때 품 => 일관성 보존
- 로그 파일에는 T1, T2 로그 레코드 섞여서 있음
- 엄격한 2단계 락킹 규약 사용해야함
# undo와 redo
1. undo : 이전 값으로 변경
- <Ti, X, V1, V2> : Ti의 로그 레코드를 Ti가 X를 V1(old)로 바꿈(update)
- 로그 파일에 <Ti, X, V> 적어줌
- 복구과정 자체도 적어줌
- <Ti abort> 적어줌 : undo 다 했다는 얘기
- abort는 취소버튼 누르거나 카밋 전에 프레임 죽으면 롤백 위해 undo 함
2. redo : 새로운 값으로 변경
- <Ti, X, V1, V2> : Ti가 X에 대해서 V2(new)를 그대로 씀
- 전체적으로 한 번만 읽으면 됨
- 로그에다 적어주진 않음 (행위는 함)
1) <Ti start> 있고, <Ti commit> or <Ti abort> 없는 경우 => undo 해줌(반대로 적음)
- 카밋 전에 값들이 버퍼공간이 모자라서 디스크로 간 경우 => undo(디스크로 안 간걸로)
2) <Ti start> 있고, <Ti commit> 있는 경우 => redo 해줌
- 디스크로 갔어야 했는데 못간 것 있을 수 있음 => 다시 한 번 더 가도록 해줌
3) <Ti start> 있고, <Ti abort> 있는 경우 => redo 해줌
- <Ti abort> 전에 undo작업 다했다는 undo 로그 레코드가 기록
- undo 작업하고 있는데 (DB에다 write 해야 함) 디스크 DB 쪽으로 안 갔었을 수도 있음
- undo 했다는 일을 다시 redo 함
- abort하면 돌려준 값이 디스크에 안 갔을 수도 있음 => 안 갔다는 사실을 기록하는 것을 한 번 더 해줌
# 검사점 (checkpoint) : 주기적으로
- 자주하게 되면 장애 발생하면 길이가 짧음
- 어디까지 위로 가야하나(checkpoint는 DB Buffer==disk 이므로 그 이후는 안 올라감)
- 시간을 줄이기 위함
- 검사점 연산이 수행되는 동안에는 모든 갱신 작업 금지
- 검사점이 수행되면 모든 수정된 버퍼 블록들을 디스크에 기록함 (flushing)
1) 현재 메인 메모리에 존재하는 모든 로그 레코드를 안정 저장 장치로 기록
2) 변경된 모든 버퍼 블록을 디스크로 기록
3) 로그 레코드 <chieckpoint L>을 안정 저장 장치로 기록(L은 검사점 시점에 동작하는 Tx)
- 로그에 <Ti commit> or <Ti abort> 없으면 undo
- 로그에 <Ti commit> or <Ti abort> 있으면 redo
- T1 : 디스크 DB에 완전히 반영 => 무시
- T2 : start, commit 있음 => redo
일부는 DB에, 나머지는 가있을 수도 안 가있을수도
살아있는(active) Tx
- T3 : start, commit 있음 => redo
- T4 : start 있는데 commit 없음 => undo
- undo (T4의 B 100으로) 내용 <T4, B, 100> 적고 <T4 abort> 적어줌
# 복구 알고리즘
# 트랜잭션 롤백
- undo 함 <Ti, Xj, V1>, <Ti abort> 적음
- redo한 다음에 undo 함
# redo phase
- 마지막 checkpoint 찾고 undo list에 checkpoint에 살아있는 Tx 넣음
- <checkpoint L> : undo_list = {L}
- 내려오다가 <Ti, Xj, V1, V2> or <Ti, Xj, V> 만나면 redo 함
- <Ti start> 있으면 Ti를 undo list에 추가
- <Ti commit> or <Ti abort> 만나면 Ti를 undo list에서 뺌
- undo phase 만날 때까지 올라감
- 가장 마지막 검사점 레코드 이후의 모든 로그 레코드들 재실행
- 히스토리 반복 : 수행된 순서와 동일한 순서로 수행 => 복구 기법 단순화시킴
# undo phase
- <Ti, Xj, V1, V2> 만나면 undo list : {Ti} => undo list에 있는 것만 undo 함
- 나머지는 undo 안함
- <Ti start> 만나면 undo_list = {∅}, <Ti abort> 적음
- undo_list 빌 때까지 작업
- undo (T2의 A 500으로) 작업 <T2, A, 500> 적고 <T2 abort> 적음
- T0는 정상적으로 처리된 작업 : 누가 취소 눌러서 다 undo 됨 <T0 abort>
# 버퍼 관리
1. 로그 레코드 버퍼링
- 블록을 안정 저장 장치에 기록하는 비용 높음 => 로그 레코드 모아서 한 번에 기록
- 로그 레코드를 메인 메모리 내에 임시로 저장
- 안정 저장 장치에 기록되는 순서 == 로그 버퍼에 기록된 순서
- <Ti commit> 로그 레코드가 안정 저장 장치에 기록된 후에 Ti 카밋 상태 됨
- <Ti commit> 로그 레코드가 기록되기 전에, Ti와 관련된 모든 로그 레코드 기록됨
- WAL(write-ahead logging. 쓰기 전 로깅) : DB 작업 전에 로그(안정 저장 장치)에 적어줌
- undo 정보만 기록. redo 정보는 나중에 기록되어도 됨
- 강제 로그 : 로그 버퍼를 디스크에 기록
2. 데이터베이스 버퍼링
- 강제 정책(force) : 트랜잭션 카밋 시 수정된 모든 블록을 강제 기록
- 비강제 정책(no-force) : 트랜잭션이 수정된 블록이 모두 디스크에 기록되지 않아도 카밋됨
- no-steal 정책 : 수정된 블록이 트랜잭션 동작 중에는 디스크에 기록되지 않음
버퍼가 가득 차는 상황 발생
- steal 정책 : 트랜잭션이 카밋되지 않아도 수정된 블록을 디스크에 기록 가능
대부분 사용
- write : 블록에 대해 독점 락 획득. 갱신 후 해제
- read : 독점 락 획득해 다른 트랜잭션이 기록하는 것 막음
모든 레코드들을 안정 저장 장치로 기록. 블록을 디스크로 기록. 기록 후 락 해제
- 래치(latch) ; 짧은 시간 동안 락 획득
- 더티 블록 : 수정되었지만 디스크에 기록되지 못한 블록
# 버퍼 관리에서 운영체제의 역할
1. 운영체제를 대신해서 데이터베이스 시스템이 메인 메모리 일부를 버퍼로 사용하고 관리
- 융통성 제한함
- 실행되지 않을 때도 메인 메모리를 자신의 버퍼로 사용하지 못함
2. 데이터베이스 시스템은 운영체제의 가상 메모리 내에 버퍼를 구현
- 버퍼 블록을 강제 기록
- 교환 공간(swap space):MM에 없는 가상 메모리 페이지를 저장하기 위한 공간 디스크에 확보
# 퍼지(fuzzy) 검사점
- checkpoint 레코드가 기록된 후 변경된 버퍼 블록이 디스크에 기록되기 전에 갱신을 시작
- 가장 마지막 검사점 => last-checkpoint라는 디스크 상의 고정된 위치에 저장
- 이를 checkpoint 레코드 쓸 때 갱신하지 않음
- checkpoint 레코드 쓰기 전 모든 변경된 버퍼 블록의 리스트 생성
- last-checkpoint는 오직 변경된 버퍼 블록 리스트의 모든 버퍼 블록이 디스크에 기록된 후 갱신
- 한 버퍼 블록이 기록되는 동안 그 버퍼 블록은 갱신 불가