본 게시글은
서울대학교 데이터사이언스대학원 이상원 교수님의
데이터사이언스 응용을 위한 빅데이터 및 지식관리시스템 수업을
학습을 목적으로 재구성하였습니다
이번 학기 데이터베이스 수업의 마지막인
DB crash recovery이다
DB recovery는 전체 아키텍처에서 저 해당 부분을 담당한다
transaction이 시작되면 각 transaction마다 ID가 부여되는데
모든 작업들은 이 transaction ID를 기준으로 수행된다
recovery도 마찬가지로 transaction ID를 기준으로 수행된다
우리가 이전 시간에
database의 ACID에 대해서 배웠는데
우리가 오늘 배울 recovery manager은
atomicity와 durability와 관련되어있다
이전 수업시간부터 계속 봤던 위 예시를 다시 한 번 보자
pi에는 100을 빼고 pj에는 100을 더하고 커밋을 한다
위와 같은 money transfer은 대부분 성공적으로 끝나지만
사용자가 전원을 끈다거나 하는 경우에 에러가 발생할 수도 있다
만약 transaction의 결과가 disk까지 내려갔다면
시스템이 전원이 갑자기 꺼지더라도 결과는 그대로 남아있게되지만
DRAM에 있는 것은 전원이 꺼지면 모두 휘발되게된다
위 ppt에는 DBMS가 죽을 수 있는 경우들이 나열되어있다
pi를 수정했는데 커밋되지 않은 채로 사용자가 롤백했다면?
이전으로 돌아가야하는 undo 작업이 필요하고
pi와 pj를 수정하고 커밋해서 commit log는 disk에 기록이 됐지만
변경된 데이터 자체는 DRAM에 있을때 crash가 발생했다면?
disk에 있는 log를 바탕으로 다시 transaction을 수행하는
redo 작업을 진행해야한다
또 pi를 수정하고 commit하기 전에 disk에 썼는데 crash가 발생했다면?
그럼 아직 commit이 되지 않았기 때문에 다시 undo를 시켜줘야한다
만약 dead lock에 걸려있다면 DBMS가
둘 중 한 transaction을 죽여야할 때가 있다
그렇게 된다면 지금까지 자기가 해왔던 것을
undo 해야할 일이 필요하다
이건 그럼 개별 transaction level에서의 recovery이고
전체 시스템 레벨에서의 recovery는 어떻게 될까?
위 예시를 한 번 살펴보자
T1, T2에서 T5까지 transaction들이 있다
그러고 저 빨간색 부분에서 crash가 발생했다
그럼 T1, T2, T3까지는 모두 다 수행이 되고 커밋도 되었으므로
큰 문제가 발생하지 않는다
이 3개의 transaction에 대해서는 따라서 수행된 결과가
crash를 복구한 이후에도 반영될 수 있는
durability가 보장이 되어야한다
그런데 T4, T5 같은 경우는 수행 중간에 crash가 발생한 것이다
따라서 중간에 하다가 멈췄기 때문에
지금까지 수행했던 T4와 T5의 모든 것들은
다시 수행하기 전의 상태로 롤백시켜야한다
이렇게 atomicity를 보장해야한다
disk write에 대해서 알아보자
위쪽은 buffer pool이고 SQL에서
insert, delete, update를 통해서 수정되는 데이터는
우선 DRAM의 buffer pool에 가장 먼저 반영된다
disk는 전원이 꺼져도 휘발되지 않는 영구 저장소로
buffer pool를 거쳐 disk로 내려가게된다
DRAM에서 disk로 가는 과정에서는 3가지의 가정이 있다
우선 첫 번째는 데이터의 변경이 일어나는 단위는
Page 단위라는 것.
보통 4KB 혹은 8KB이다
두 번째는 update 방법에 대한 것인데
in-place update는 기존의 페이지를 덮어쓰는 방법으로
빠르지만 나중에 복구하기가 어렵다는 단점이 있다
두 번째는 out-of-place 혹은 copy-on-write라고 불리는 방법인데
이는 disk에 overwrite를 하지 않고 새로운데다가 append해서 쓰는 방법을 말한다
그러다가 transaction이 commit이 되면 그때 교체를 하는 방법을 말한다
저장 공간을 많이 필요로 하지만 안전하게 저장할 수 있다
그런데 여기서 buffer 자체는 공간이 한정되어있기 때문에
페이지를 교체하는 buffer replacement가 발생하는데
이때는 disk에 내려놓은 다음 교체를 진행해야한다
buffer에서 disk로 쓰는 방식에 대해서 알아보자
우선 설명은 단일 트랜잭션을 기준으로 설명된다
transaction을 커밋할 때 update한 모든 페이지를
다 disk에다 써버리는게 force write at commit이라고 한다
이렇게 하면 transaction이 update한 모든 페이지를
강제로 disk에 내리게 되는데
당연히 매우 단순하고 매우 간단하게 durability가 보장이된다
하지만 너무도 당연하게 이런 식으로 하면 문제가 발생한다는 점인데
disk IO가 많이 발생한다는 것이다
commit 시에 바로 disk에 내리는게 force 정책이었다면
이제 steal 정책에 대해서 알아보자
steal은 buffer가 꽉 차서 더이상 새로운 페이지를 저장할 수 없으면
commit되지 않아도 disk로 내리는 방식을 말한다
이렇게 되면 쿼리의 중간과정을 disk에 내릴 수 있어
buffer 공간을 효율적으로 관리할 수 있게 된다
하지만 만약에 수행하다가 commit 이전에 시스템 에러가 나서
롤백을 해야하는 경우에 문제가 발생한다
따라서 steal과 no-force 정책으로 수행하면
훨씬 더 많은 commit을 처리할 수 있어서 퍼포먼스가 좋아진다
하지만 이렇게 되면 recovery를 해야할 일이 많아지고
DBMS가 고려해야할 일이 매우 많아져서
recovery 구현 자체가 복잡해지게 된다
위에서 설명한 steal 정책을 사용하면
rollback시에 다시 데이터를 복원시키기 위해서
undo log를 활용해서 되돌린다
따라서 steal을 하면 undo를 관리해야하고
no-force를 하면 disk에 쓰기 전에 crash가 날 경우를 대비해서
redo를 관리해야한다
따라서 DBMS는 우리가 모르는 사이에
계속해서 현재 상황을 캡처해서 관리하고있다
그렇다면 No-force 정책을 수행했는데
commit은 되었는데 disk에 내려가기 전에 crash가 발생했다면
이를 어떻게 복구할까?
이런 경우에는 log를 확인해서 redo를 수행한다
log에 해당 트랜잭션이 커밋되었다고 되어있는데
디스크에 데이터가 없음을 확인하면
redo log를 따라 해당 트랜잭션을 다시 적용한다
그리고 앞서도 설명했지만
steal일 때 crash가 발생해서
commit 이전인데도 해당 데이터가 디스크에 쓰여졌다면
undo log를 이용해서 다시 이전으로 되돌린다
따라서 이 undo를 위해서 log를 다 캡처해야한다
앞서 간단하게 설명했던 내용이다
force로 하면 commit시 바로 disk에 데이터를 쓰기 때문에
매우 간편하고 durability를 간단하게 보장 가능하지만
diskIO가 증가해서 효율성이 떨어진다
또 반면에 steal 정책을 사용하면 buffer 효율성은 좋아지지만
atomicity 보장이 복잡해진다
따라서 위 2가지 문제를 해결하기 위해서
DBMS는 매 순간 log를 캡쳐해서 사용한다
다음 챕터에서는 log에 대해서 자세하게 알아보자
이 챕터에서 우리가 알아야할 것은 무엇인가
일반적으로 disk가 있고 cache가 있고
별도로 로그를 관리하는 log buffer가 DRAM 이외에 따로 존재한다
모든 Log는 자기의 LSN 주소를 갖고있다
위 ppt는 oracle의 구조인데 대부분의 DBMS가 갖고있는 구조기도 하다
보통의 DBMS는 force-log-at-commit을 redo recovery로 사용하고
Write-Ahead Log(WAL)을 undo recovery로 사용한다
DBMS는 모든 transaction에 대해서 어떤 일을 했구나를
redo, undo log로 남긴다
앞에서 설명했듯이
update-in-place + steal + no-force 전략을 사용하면
성능면에서는 굉장히 효율적이지만
recovery 구현이 매우 복잡해진다
따라서 recovery를 위해서 undo와 redo log를 모두 남겨야한다
그런데 하나의 데이터 페이지에는
여러 개의 transaction들이 섞여있어 recovery가 힘들 수 있다
그래서 pageLSN이란 것을 이용하는데
모든 log에는 LSN(Log Sequence Number)이라는 주소가 존재한다
따라서 해당 log가 적용된 page에는 LSN이 부여되는데
이게 pageLSN이다
또한 log도 log buffer라는 DRAM 영역에 우선적으로 저장이되는데
따라서 log를 언제 디스크로 안전하게 보낼 것인지
시점도 매우 중요해진다
한 페이지 내부에서 데이터가 어떻게 저장되고
데이터가 insert, update, delete가 발생할 때마다
page log가 어떻게 저장되는지를 나타내는 ppt이다
데이터들은 각 슬롯마다 저장이 되어있고
해당 page에 log가 적용될때마다
(LSN, Tid, Pi, Offset, Lenght, Before-value, After-value)의 형태로
저장이 된다
DBMS에서 사용하는 로그는
3가지 종류가 있다
physical log, logical log, physio-logical log인데
physical log는 변경된 실제 바이트값 자체를 기록하고
logical log는 무엇을 했는지 명령어 수준으로만 기록하고
physio-logical log는 위의 2가지를 동시에 갖고있는 로그이다
physical log는 쉽게 설명해서 몇 번째 얼마얼마의 값
이런 구체적인 정보가 적혀있는 것이고
logical log는 어떤 값이었다가 어떤 값으로 이런 명령어를 통해 바꿨다
와 같은 식으로 정보가 적혀있는 것이다
위 ppt에서 왼쪽은 logical log가 적혀있는 모습이고
오른쪽은 physio-logical log가 적혀있는 모습이다
지금까지 배웠던 Log에 대한 내용 정리이다
database는 disk의 Page와 log로 구성되어있다
DRAM에 있는 page들은 전원이 꺼지면 사라지기때문에 안전하지않다
따라서 Log를 통해서 DBMS의 atomicity와 durability를 보장하도록 하는 것이다
혹시나 DBMS에서 system crash가 발생하더라도
redo log를 통해 durability를 보장하고
undo log를 통해 atomicity를 보장한다
위는 DO-UNDO-REDO를 시각적으로 모델링한 것이다
어떤 Transaction이 실행되어서
OLD에서 NEW로 DO가 되면
undo log와 redo log가 동시에 기록된다
redo log는 OLD에서 NEW를 반영할 떄 사용되고
undo log는 NEW에서 다시 OLD로 rollback할 때 사용된다
뭐 이런 로그가 헨젤과 그레텔에서의 과자..?
역할을 한다고 한다 ㅋㅋ
로깅에는 2가지 핵심 프로토콜이 있다
첫 번째는 undo log에서의 WAL인데
데이터 페이지를 disk에 쓰기 전에
해당 변경에 대한 undo log가 반드시 먼저 디스크에 기록되어야한다는 것이다
데이터가 디스크에 먼저 쓰였는데 그때 시스템이 죽어서
log가 없다면 다시 복원이 불가능하기 때문이다
DBWR(database writer)가 쓰기전에 LGWR(log writer)에
log flush를 먼저 요청한다
이 overhead는 그렇게 크지 않다고 한다
두 번째는 redo log에서의 Log-Force-at-Commit이다
트랜잭션이 commit되면 log가 disk에 안전하게
저장이 되어야한다는 것이다
그래야 시스템이 죽어도 안전하게 redo를 수행할 수가 있다
하지만 이는 commit 때마다 disk에 저장하므로
disk IO가 발생해서 병목이 발생할 수가 있다
그래서 이를 예방하기 위해서 group commit이라는 해결책이 있는데
이는 commit이 일정 개수 이상 모이면
한 번에 disk에 저장하는 방법이다
oracle DB의 아키텍처이다
WAL 프로토콜을 시각화한 것이다
pj를 disk에 쓰기 전에 log buffer에 있는 log를
먼저 disk로 내리는 것이다
log force at commit이다
transaction이 commit이 되면 log buffer에 있던 log가
disk로 내려가는 것이다
마지막으로 checkpointing이라는 개념을 알아보고
recovery 챕터 및 이번 학기 DBMS 수업을 마무리해보려고한다
앞에서 DBMS의 성능을 위해서
no-force 정책을 사용한 다음 system crash가 발생하면
redo log를 이용해서 복구를 했다
하지만 만약에 redo할게 너무 많다면 시스템 성능이 떨어질 수가 있다
이런 경우를 예방하기 위해서
일정 시점마다 buffer의 내용을 disk에 저장해두는데
이를 checkpointing이라고 한다
우리가 LLM 모델 학습시킬 때
일정 epoch만큼 학습시키면 checkpoint라고 해서
해당 버전을 저장해두곤하는데
그 checkpoint와 유사한 개념이다
checkpoint 작업을 할 때는 우선
작업이 언제 시작되었는지를 나타내는
begin_checkpoint를 기록한다
그다음에는 end_checkpoint를 기록하는데
이는 정확한 정보는 아니다
왜냐하면 해당 end_checkpoint를 기록하는 시점에도
트랜잭션은 계속해서 진행되고 있고
따라서 해당 end_checkpoint는 begin_checkpoint 시점에서의
snapshot일 뿐 정확한 데이터가 아닐 수 있다
또한, 모든 dirty page를 disk에 쓰는 것이 아니라고 한다
원칙상 모든 dirty page를 disk에 쓰면 좋겠지만
실제로는 그렇게 하지 않고
현재까지 진행된 transaction의 일부만 disk에 내린 다음
DBMS가 성능에 맞도록 알아서 나머지들도 disk에 내린다고한다
이걸 그래서 정확하지 않기 때문에
fuzzy checkpoint라고 한다
요약이다
지금까지 WAL 프로토콜과 Force-Write Log를 배웠고
checkpointing을 배웠다
ARIES는 현대 DBMS의 표준 recovery 알고리즘이다
뭐 이런 것들이 있는데 크게 설명하지는 않으셨다
시간이 나면 한 번 읽어보라고 하셨다
아무튼 이렇게 recovery까지 마지막으로해서
이번 학기 DBMS 수업이 모두 끝났다
마지막으로 갈수록 사실 집중도가 좀 떨어져서..
뭔소린지 잘 몰랐던 부분도 많았지만..
어쨌든 끝-!
'강의 > database' 카테고리의 다른 글
[database] DB Lock 2편 (multi-version CC, MVCC) (2) | 2025.06.06 |
---|---|
[database] DB Lock 1편 (1) | 2025.06.03 |
[database] simpleDB로 buffermanager에서 LRU 알고리즘 구현하기 (2) | 2025.05.27 |
[database] Physical Query Algorithm & Query Optimization (0) | 2025.05.26 |
[database] External Sorting (2) | 2025.05.25 |