06 문장수준 읽기 일관성
읽기 일관성은 중요하다..
단일 SQL문이 수행되는데 있어서, 읽기 일관성은 당연히 매우 중요하다.
그래서 보통은, 읽기를 진행할 때에도 row lock을 거는 DBMS도 종종 존재하긴 한다.
그런데, 단일 row를 읽는 경우라면 모를까, 사실 row lock만으로는 부족하다.
| 계좌번호 | 잔고 |
|---|---|
| 1 | 1000 |
| 2 | 1000 |
| 3 | 1000 |
| 4 | 1000 |
| 5 | 1000 |
| 6 | 1000 |
| 7 | 1000 |
| 8 | 1000 |
| 9 | 1000 |
| 10 | 1000 |
이 테이블 구조에서,
케이스 1
트랜잭션 1이 다음과 같은 쿼리를 수행하고
SELECT SUM(잔고) FROM 계좌
고 사이 트랜잭션 2가 다음과 같은 쿼리를 수행하면..
INSERT INTO 계좌(계좌번호, 잔고) VALUES (11,1000);
트랜잭션 1에서 계좌번호 1~10를 다 row lock을 건다 한들, 읽기 일관성이 보장되지 않는 경우가 생겨버리네…
- 트랜잭션 1이 레코드를 다 읽기 전에 트랜잭션 2가 커밋 찍히면 –> 계좌번호 11의 값까지 읽은채로 수행됨
- 트랜잭션 1이 레코드를 다 읽은 후에 트랜잭션 2가 커밋 찍히면 –> 계좌번호 10의 값까지 읽은채로 수행됨
케이스 2
트랜잭션 2가 갑자기 트랜잭션1이 읽고 지나간 레코드에서 잔고를 차감하거나 더하면… 총 합계가 더하거나 뺴져야 하는 경우가 발생할 수 있다.
p.s: 책에서는 다른 DBMS를 예를 들어서 ‘다른 DBMS는 이 읽기 일관성이 보장이 되지 않는다’ 라고 하는데… 이건 패스하기로 함..
고러면 어떻게 해야 하나??
가장 심플한 해결법 : 트랜잭션 고립(Isolation) 수준을 높이거나, 테이블 lock을 걸면 된다..
그런데, 이러면 성능이 당연히 나오지 않음.
그럼 오라클은 어떻게 이 읽기 일관성을 보장하는가?
UNDO세그먼트를 이용해 읽기 일관성을 보장한다.
특히, SCN이라고 하는 시간정보를 이용해 일관성 보장을 하는데, 그냥 ‘커밋번호’ 라고 이해하면 된다.. (애초에 System Commit Number 임.) 커밋을 찍을때마다 이 값은 증가하는 방식으로 늘어나고, 각 블록들은 이 SCN을 갖고 있다고 생각하면 됨.

원리는 간단하다. ‘읽는 쿼리를 날린 시점의 SCN보다 작거나 같은것들 중, 가장 큰’ SCN블록의 값을 읽어들인다.
쿼리를 날리는 시점의 SCN이 123 일때
- SCN이 123보다 큰 블록은 ‘내가 읽기-SELECT쿼리-를 시작한 이후 시점에 적용된 블록’ 이다. 따라서 읽지 않는다.
- SCN이 123보다 작은 블록은 ‘내가 읽기를 시작한 시점에 이미 기 적용되어있는 블록’이다. 따라서 읽어야 한다.
- 허나, 그 작은 블록들 중 아무거나 읽으면 안되고, 그 중에서 가장 마지막에 적용된 블록의 값을 읽어야 한다.
책에선 엄청 많은 복잡한 상황을 대입하며 설명을 너줄너줄 하지만, 사실 핵심은 이거 하나임…
그럼 내가 읽으려는 블록이 UNDO세그먼트에서 사라지면?
당연히 읽지 못한다. 그때 발생하는 ORA오류가 있다. ‘Snapshot too old’
- 내가 날린 쿼리가 일단 엄청 헤비한 쿼리이다.
- 하필 트래픽이 많은 시간대이고 커밋이 잦은 상황이라서, DB가 계속 새로운 SCN으로 업데이트 되며 UNDO에 내가 읽으려는 데이터가 밀린다.
- 쿼리가 다 풀려 이제 블록을 읽으려 하는데, UNDO세그먼트에서 이미 용량부족으로 인해 날아가버린 블록이 있으면 에러 발생
07 Consistent vs Current 모드 읽기
07 대제목에 나와있듯, 블록 읽기 모드에도 여러가지 모드가 있음.
- Consistent mode
- ‘쿼리를 떄린 그 시점의 SCN값을 기준으로 값을 읽음’
- Current mode
- ‘쿼리를 떄리고 나서, 데이터를 찾아간 바로 그 시점의 최종 값을 읽음’
근데… 앞선 SCN이 어쩌고저쩌고 하는걸 보면… 단순이 SELECT하는 쿼리문은 Consistent모드로 읽지 않나?
그렇지 않음… DML을 할 때에도 “UPDATE SET A = A+1” 과 같이 이전 값을 읽어야 할 시점이 있을거고, SELECT FOR UPDATE문과 같이 UPDATE 직전에 데이터를 읽는 경우도 있음. 이때 바로 오라클은 Current mode를 통해 데이터를 읽는다고 한다.
자세한 내용은 모르겠고… 그저 ‘일반적인 단순 SELECT는 Consistent mode로 데이터를 읽지만, 다음과 같은 예외 케이스에 Current mode로 데이터를 읽는 경우도 있다’ 정도를 외워두자.
- DML
- SELECT FOR UPDATE
- 디스크 소트가 필요할 정도로, 대량의 데이터를 정렬할 때
- 등등등등….
책에서 갑자기 별로 필요할 것 같지도 않은 Current mode에 대해서 너줄너줄 설명을 하기 시작한다..하지만 Current mode가 필요한 경우가 분명 존재한다.