본 게시글은
서울대학교 데이터사이언스대학원 이상원 교수님의
데이터사이언스 응용을 위한 빅데이터 및 지식관리시스템 수업을
학습을 목적으로 재구성하였습니다
오늘은 DB lock의 두번째 시간인데
multi-version concurrency control에 대해서 주로 배운다
수업 내용이 저번 시간이랑 겹치는 내용이 많아서
그 부분들은 생략하고 MVCC에 대한 내용만 담아서
내용이 조금 짧다
우리가 이전 시간까지 수업에서 설명한 내용은
single version의 concurrency control이다
lock의 종류는 크게
read lock인 shared lock과
write lock인 exclusive lock이 있고
이 lock compatibility table을 통해서
각각 어떻게 호환되는지를 알아보자
우선 R(S)와 R(S)간에는 당연히 호환이 가능하다
읽기 lock이 걸려있을 때 다른 트랜잭션이 읽는 것은 상관이없다
그러나 R(S)가 걸려있을 때 데이터를 수정하려는 W(X)는 호환이 안되며
데이터를 수정하려고 W(X)가 걸려있을 때는 당연히
R(S)도 W(X)도 허용되지 않는다
또한 lock based concurrency control의 성능을 높이기 위해서
isolation level을 4단계로 나누어 관리한다
격리수준이 가장 높은 serializable로 가면
동시성은 낮아지지만 데이터 일관성이 높아지고
격리수준이 가장 낮은 read uncommitted로 가면
동시성은 높아지지만 데이터 일관성이 낮아질 수 있다
각 isolation level별로 발생할 수 있는
이상현상(anomaly)들을 위 ppt의 표에 정리해놨다
아무튼 single version의 입장에서는 이런식으로
4단계의 레벨을 도입해서 lock을 지원을 한다
그렇다면 oracle은 어떻게 concurrency control을 지원하고 있을까
원래 CC는 IBM에서 single version으로 지원하고있었는데
oracle이 IBM에 대적하기 위해(?)
multi-version concurrency control을 만들었다고한다
이 MVCC는 timestamp 기반으로 각각의 레코드들이
multi version을 갖게 된다
따라서 오라클에서는
우선 1) lock을 쓰면
2) 데이터는 다양한 버전을 갖고있고
3) 이를 timestamp 기반으로 어느 시점에 commit한 것을
읽을 것인지를 결정한다
이때 버전마다 갖고 있는 timestamp가
commitSCN(System Change Number)이다
대부분의 DBMS는 다 multi-version으로
lock을 관리한다
일반 single version이랑 multi version은
commit을 하고 그런 과정들은 다 동일하지만
다른 점은 single version은 isolation level이 4개가 있지만
multi version은 isolation level이 read committed, serializable 2개밖에 없다
왜냐하면 나머지는 필요가 없기 때문이다
이번엔 Oracle에서 dead lock을 어떻게 관리하는지 보자
blocking 당한 상태로 끝없이 기다리고 있는 상태를
dead lock이라고 한다
그렇다면 oracle은 현재 dead lock이 걸려있는지 어떻게 알 수 있을까?
DB는 dead lock이 발생했는지 아닌지를 주기적으로 체크한다
가는 길에 cycle을 체크해서 만약 Dead lock 상황이라면
둘 중에 한 놈을 kill 시킨다
그런데 생각해보면 사실 dead lock을 미리
prevention 할 수도 있다
만약 내가 지금 이 lock을 실행하면 dead lock이 걸린다는 것을 감지하고
실행하지 않도록 할 수 있다
하지만 이러한 방식대로 구현하려면 굉장히 복잡해지고
dead lock일 확률은 사실 되게 낮은데
그걸 검사하기 위해서 추가적인 연산을 필요로 하게 된다
그래서 일반적으로는 dead lock이 발생하면 detect를 하고
둘 중 한개를 kill 시키는 방식을 사용하고
oracle도 이런 방식을 채택하고있다
oracle은 read lock을 걸지않는다
그리고 같은 페이지의 여러 버전을
buffer pool에 복제해놓는다
그런 다음 변경되기 전 버전을 읽어서
dirty read를 방지한다
또한 트랜잭션이 시작한 시점의 고정된 버전만 계속 읽어서
repeatable read도 보장한다
오른쪽 예시의 경우 A에서 50을 빼고 B에 50을 더하는
트랜잭션이 있다고 가정하자
그런데 A에서 50을 뺐을 때 sum이라는 select를 했다면
기존의 DB라면 dirty read가 발생하게된다
하지만 oracle은 A에 트랜잭션이 수행되기전과 후 버전이 있기 때문에
sum read를 할 때 A에 트랜잭션이 수행되기 전 버전을 가져와서
데이터의 일관성을 유지할 수 있는 것이다
그렇다면 phantom read는 어떤 방식으로 방지할까?
각 트랜잭션과 레코드는 commit 할 때 SCN이 부여된다
따라서 SCN을 기준으로 필터링이 되어서
select시 해당 시점 이전에 커밋된 레코드만 조회해서
새로운 레코드가 나오는 것을 방지한다
지금까지 oracle은 트랜잭션이 시작될 때의 시점(SCN)을 기준으로
데이터를 보장한다는 것을 배웠다
만약 데이터가 이후 트랜잭션에 의해서 수정되었더라도
undo segment에 저장된 이전 데이터를 보여준다
정리하자면 oracle은 Read lock을 걸지 않는다
reader가 writer를 blocking하지 않고
데이터 일관성을 위해서 수정된 레코드의 예전 버전들을
undo segment에 다 저장해놓는다
그 다음 트랜잭션 시작 시점인 SCN 이후의 레코드들은 무시하고
이전 상태의 데이터만 보여주게 되는데
이로써 일관된 snapshot을 제공할 수 있다
그렇다면 이전 버전의 데이터들을
언제까지 저장할 수 있을까?
undo segment의 저장용량이 꽉 차서
더이상 데이터를 저장할 수 없을 경우
DBMS는 그냥 과거의 데이터를 지워버린다
이 상황에서 트랜잭션이 지워버린 버전의 데이터를 요구하면
그때 DBMS는 snapshot too old라고 하는
일종의 배째라(?) 에러를 내버린다고 한다
뭐 지금은 디스크 가격이 싸서 저런 경우가 많이 없다고 하는데
예전에 리소스를 제한할 때에는
저런 에러메시지를 많이 띄웠다고 한다
oracle에서의 isolation level과
ANSI 표준의 isolation level을 비교해보자
앞에서 말햇지만 일반적인 isolation level은
4개의 단계로 나뉜다
하지만 oracle같은 경우는 MVCC를 지원하기 때문에
2개의 level은 필요가 없어져서
serializable과 read committed만 남게 된다
'강의 > database' 카테고리의 다른 글
[database] Crash Recovery (2) | 2025.06.07 |
---|---|
[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 |