데이터베이스에서 읽기 일관성(Read Consistency)이란?
쿼리를 실행하는 동안, 해당 쿼리가 보는 데이터는 “논리적으로 일관된 하나의 시점”이어야 한다는 원칙을 말한다
즉, 내가 SELECT를 실행했을 때 조회되는 데이터는 쿼리가 시작된 시점의 상태로 고정되고,그 이후 다른 트랜잭션이 데이터를 변경하더라도 내 SELECT 결과에는 반영되지 않아야함
단일 SQL문이나 트랜잭션 내에서 일련의 SQL문이 차례로 수행되는 도중에 다른 트랜잭션에 의해 데이터가 변경, 추가, 삭제된다면 일관성 없는 결과집합을 리턴하거나 값을 잘못 갱신하는 문제가 발생할 수 있다. 이런 현상들을 방지하고 읽기 일관성을 보장하기 위해 DBMS마다 트랜잭션 읽기 일관성, 문장수준 읽기 일관성등을 제공한다.
1) 문장 수준 읽기 일관성이란?
단일 SQL문이 수행되는 도중에 다른 트랜잭션에 의해 데이터의 추가, 변경, 삭제가 일어나더라도 일관성있는 결과집합을 리턴하는 것을 말함
Dirty Read 허용하면 읽기 일관성 보장 x
-> 다른 DBMS들은 로우 Lock을 사용해 Dirty Read 방지
오라클은 Lock을 사용하지 않고 Undo 세그먼트에 저장해 둔 Undo 데이터를 활용하므로 완벽한 문장수준 읽기 일관성을 보장
다른 DBMS들은 Lock을 사용해 읽기 일관성을 구현하는데, 기본 트랜잭션 고립화 수준(lv1, Read Commited)에서는 값을 읽는 순간에만 Shared Lock을 걸었다가 다음 레코드를 이동할 때 Lock 해제
-> Shared Lock이 해제되기 때문에 이미 읽고 지나간 레코드를 다른 트랜잭션이 변경할 수 있게 되고, 이 때문에 문장수준 읽기 일관성이 보장되지 않는 문제가 생긴다
-> 트랜잭션 고립화 수준을 상향 조정하면 Lock이 발생하는 범위가 넓어지고 더 오래 유지되기 때문에 동시성 저하를 초래함 + 교착상태 발생 가능성 높아짐
(2) Consistent 모드 블록 읽기
Current 블록 : 디스크로부터 읽혀진 후(데이터를 찾아간 바로 그 시점의 최종 값) 사용자의 갱신사항이 반영된 최종 상태의 원본 블록
Consistent 블록 : 쿼리가 시작된 시점을 기준으로 데이터를 읽음
-> CR 블록은 Current 블록의 복사본임, CR블록은 여러개 가능 Current 블록은 단 하나
오라클의 MVCC 모델
=> 오라클은 Current 블록을 CR Copy 블록으로 복사하여 읽기 일관성을 지원함
데이터를 다중 버전 상태로 유지하고 있어 Commit전에 Undo 영역에 유지하고 있기 때문
그러면 오라클에서 Consistent 모드로 데이터를 읽을 떄, 쿼리 시작 시점과 블록의 마지막 변경 시점을 어떻게 확인할까?
1) 쿼리 시작 시점
오라클은 SCN(System Commit Number)이라고 하는 시간정보를 이용해 데이터베이스의 일관성 있는 상태를 식별, 쿼리 SCN이라 부름
2) 블록 마지막 변경 시점
오라클은 블록이 마지막으로 변경된 시점 정보를 식별하기 위해 모든 블록 헤더에 SCN(System Change Number) 정보를 관리하는데 블록 SCN이라 부름
3) Consistent 모드 읽기의 세부원리
Consistent 모드에서 데이터를 읽을 때 쿼리 SCN을 들고다니면서 블록 SCN과 비교해 읽을 수 있는지 판단함
1. Current 블록 SCN <= 쿼리 SCN이고, committed 상태
2. Current 블록 SNC > 쿼리 SCN이고, committed 상태
1. Current 블록의 블록 SCN이 쿼리 SCN보다 크면 CR Copy 블록을 먼저 생성 (CR Cloning)
2. CR 블록은 쿼리 SCN 보다 작은 블록 SCN의 과거 버전으로 되돌림
블록 SCN <= 쿼리 SCN 조건을 만족할 때까지 과거 버전으로 되돌리기를 반복
완성된 CR 블록은, 블록 SCN이 쿼리 SCN보다 작거나 같으면서 커밋되지 않은 내용은 전혀 포함하지 않은 상태가 됨
3. Current 블록이 Active 상태, 즉 갱신이 진행 중인 것으로 표시 돼 있을 때
1. Consistent 모드 읽기
2. Current 모드 읽기
이 블록들이 나타나는 상황들
Consistent 모드는 쿼리 시작 시점을 기준으로 값을 읽기 때문에 이 값을 기준으로 갱신하면 Lost Update가 발생하는 결과를 초래한다.
이런 Lost Update 문제를 회피하려면 갱신 작업만큼은 Current 모드를 사용해야 한다.
예를 들면, UPDATE가 이미 진행된 블록에 INSERT된 레코드는 갱신 대상에서 제외되고, 아직 UPDATE 되지 않은 블록에 INSERT된 레코드는 갱신 대상에 포함된다.
또 다른 예로는, "DELETE FROM LOGG" 문장이 수행되는 도중에 다른 트랜잭션에 의해 새로 추가된 로그 데이터까지 지워질 수 있다.
요약하자면?