REPEATABLE READ CLASS with REPEATABLE READ INSTANCES
이 격리 수준에서는 더티 읽기, 반복 불가능한 읽기가 발생하지 않지만, 유령 읽기는 발생할 수 있다.
다음과 같은 규칙이 적용된다.
- 트랜잭션은 다른 트랜잭션에서 수정 중인 객체를 덮어쓰지 않는다.
- 트랜잭션은 트랜잭션이 끝날 때까지 객체를 커밋하지 않는다.
- 트랜잭션은 다른 트랜잭션에서 수정 중인 객체를 읽지 않는다.
- 트랜잭션은 반복 가능한 읽기를 보장하기 위해 모든 공유/배타 잠금을 트랜잭션 종료 때까지 놓지 않는다.
- 다른 트랜잭션은 현재 트랜잭션이 종료하기 전까지 현재 트랜잭션이 읽은 객체들(특정 클래스의 인스턴스들 혹은 특정 조건을 만족하는 인스턴스들)에 속하는 객체들을 추가하지 못한다.
이 격리 수준은 2단계 잠금 프로토콜을 따른다
예제
다음 예제는 이 격리 수준에서 한 트랜잭션은 어떤 클래스에 인스턴스를 삽입하고, 다른 트랜잭션은 특정 검색 조건으로 검색하는 트랜잭션이 있는 경우이다. 트랜잭션 T1은 임의의 트랜잭션 격리 수준이고, T2는 REPEATABLE READ CLASS with REPEATABLE READ INSTANCES 격리 수준이다. 여기서 participant2 클래스는 기존에 생성되지 않은 것으로 가정한다.
- T1이 participant2 클래스를 생성하고 host_year, nation_code 속성에 유일 키를 생성하고 몇 개의 인스턴스들을 삽입한다. T1이 커밋한 후에 CUBRID는 T1에 의해 획득된 모든 잠금들을 해제한다. 그리고, T2가 participant2에 대해 nation_code = 'AUS' 라는 조건으로 검색을 하였다.
- User1 (T1):
- SET TRANSACTION ISOLATION LEVEL REPEATABLE READ CLASS, REPEATABLE READ INSTANCES;
;xrun
;autocommit off
create class participant2 (host_year integer, nation_code char(3));
create unique index on participant2 (nation_code, host_year);
insert into participant2 (host_year, nation_code) values (2008, 'AUS');
insert into participant2 (host_year, nation_code) values (2008, 'JPN');
insert into participant2 (host_year, nation_code) values (2008, 'BRZ');
insert into participant2 (host_year, nation_code) values (2008, 'CHN');
insert into participant2 (host_year, nation_code) values (2004, 'AUS');
commit work;
;xrun
1 rows inserted.
1 rows inserted.
…
- User2 (T2):
- SET TRANSACTION ISOLATION LEVEL REPEATABLE READ CLASS, REPEATABLE READ INSTANCES;
;xrun
;autocommit off
select host_year, nation_code from participant2 where nation_code='AUS' and host_year=2008;
;xrun
=== <Result of SELECT Command in Line 1> ===
host_year nation_code
===================================
2008 'AUS'
1 rows selected.
- 이때, T1이 (2004, 'KOR')인 인스턴스와 (2000, 'NED')인 인스턴스를 participant2 클래스에 삽입하려고 하면, T1과 T2가 서로 같은 인스턴스에 대한 잠금을 갖지 않으므로 T1은 즉시 진행할 수 있다. 여기서 (2004, 'KOR'), (2000, 'NED') 인 인스턴스는 T2에게는 유령 인스턴스로 보일 수 있다. T2가 다시 같은 질의문을 수행하면 T1이 삽입한 인스턴스를 보게 된다.
- User1 (T1):
- insert into participant2 (host_year, nation_code) values (2004, 'KOR');
insert into participant2 (host_year, nation_code) values (2000, 'NED');
commit work;
;xrun
1 rows inserted.
1 rows inserted.
- User2 (T2):
- select * from participant2 where nation_code='AUS' and host_year=2008;
;xrun
1 rows selected.
- T1이 (2008, 'AUS') 인 인스턴스를 삭제하는 질의를 수행하면, T2가 (2008, 'AUS') 에 대해서 아직 커밋하지 않고 공유 잠금을 잡고 있는 인스턴스가 있으므로 대기한다. T2가 커밋하면 모든 잠금이 해제되어 T1이 수행된다. 다시 T2가 같은 질의문을 수행하면 T1이 삭제한 인스턴스 때문에 대기한다.
- User1 (T1):
- delete from participant2
where nation_code = 'AUS' and
host_year=2008;
;xrun
- User2 (T2):
- commit work
;xrun
------
select * from participant2
where nation_code= 'AUS';
;xrun
- 다시 T1이 커밋하면 잠금 들이 해제되면서 T2가 수행되어 SELECT 문의 결과를 볼 수 있게 된다.
- User1 (T1):
- commit work
;xrun
- User2 (T2):
- === <Result of SELECT Command in Line 1> ===
host_year nation_code
===================================
2004 'AUS'
- 다음에 T1이 participant2 클래스를 nation_medals 클래스로 이름을 갱신하려고 하면 T2가 커밋되지 않았기 때문에 대기상태가 된다.
- User1 (T1):
- rename class participant2 as nation_medals;
;xrun
- T2가 커밋하면 T1이 다시 진행되어 participant2 클래스를 nation_medals 클래스로 갱신한다. T1이 커밋되고, T2가 다시 수행되면 participant2 클래스는 더 이상 존재하지 않으므로 구문 에러를 발생시킨다.
- User2 (T2):
- commit work;
select * from participant2 where nation_code = 'AUS';
;xrun
In line 2, column 16,
ERROR: Class participant2 does not exist.
- User1 (T1):
- commit work;
;xrun