READ COMMITTED CLASS with READ UNCOMMITTED INSTANCES
이 격리 수준은 트랜잭션이 다른 트랜잭션에서 수정 중인 클래스를 읽지 못하게 하며, 이후에 갱신되거나 롤백될 수도 있는 더티 인스턴스를 읽도록 허용한다. 트랜잭션은 클래스와 인스턴스에 대한 읽기가 반복 불가능(non-repeatable read)할 수도 있다. 이것은 트랜잭션이 동일한 클래스를 두 번 읽게 되면 다른 두 커밋된 값을 읽을 수 있고, 동일한 인스턴스를 두 번 읽게 되면 커밋되었거나 그렇지 않은 서로 다른 두 값을 읽을 수 있음을 의미한다. 커밋되지 않은 인스턴스는 그 인스턴스를 갱신하는 트랜잭션에 의해 롤백될 수
있는데, 그 이유는 다른
트랜잭션이 두 번 읽는 간격 동안에 객체를 갱신하기 때문이다. 마찬가지로, 한 질의가 두 번 수행되면 두 수행 결과가 다를 수 있다. 이것은 두 질의가 수행되는 간격 동안에 커밋되거나 그렇지 않은 인스턴스에 대한 삽입, 갱신, 삭제가 있을 수 있기 때문이다. 질의되는 클래스는 더 이상 존재하지 않을 수도 있고 속성 일부는 추가되었거나 삭제되었을 수도 있다. REPEATABLE READ 격리 수준에서는 클래스에 대한 갱신은 불가능한데, 그 이유는 트랜잭션이 끝까지 클래스에 대한 잠금을 가지고 있기 때문이다.
아래는 이 격리 수준의 규칙이다.
- 트랜잭션은 다른 트랜잭션에서 수정 중인 객체를 덮어쓰지 않는다.
- 트랜잭션은 트랜잭션이 끝날 때까지 객체를 커밋하지 않는다.
- 트랜잭션은 다른 트랜잭션에서 수정 중인 클래스를 읽지 않는다. 이 규칙은 인스턴스에는 적용되지 않는다.
이 격리 수준은 배타 잠금에 대해서 2단계 잠금을 한다. 하지만 공유 잠금은 인스턴스에 대해 획득할 수 없고 클래스에 대한 공유 잠금 또는 의도 공유 잠금은 클래스가 읽혀진 후 곧바로 해제된다.
예제
한 트랜잭션은 인스턴스를 삽입하고 클래스를 갱신하며 다른 클래스는 다양한 관점에서 클래스에 질의를 한다. 트랜잭션 T1은 임의의 격리 수준을 가지며 트랜잭션 T2는 READ COMMITTED CLASS with READ UNCOMMITTED INSTANCES 격리 수준을 가진다. 이 예제에서는 partition2 클래스가 이전에 존재하지 않을 것으로 가정하고 생성한다.
- 트랜잭션 T1은 partition2 클래스를 생성하고 인스턴스를 삽입한다. 트랜잭션 T1이 커밋되자마자 CUBRID는 트랜잭션 T1이 가지고 있던 잠금을 해제한다. 트랜잭션 T2는 partition2 클래스에 질의한다.
- User1 (T1):
- SET TRANSACTION ISOLATION LEVEL READ COMMITTED CLASS, READ UNCOMMITTED INSTANCES;
;xrun
;autocommit off
create class participant2 (host_year integer, nation_code char(3));
insert into participant2 (host_year, nation_code) values (2008, ‘AUS’);
commit work;
;xrun
1 rows inserted.
- User2 (T2):
- SET TRANSACTION ISOLATION LEVEL READ COMMITTED CLASS, READ UNCOMMITTED INSTANCES;
;xrun
;autocommit off
select host_year, nation_code from participant2;
;xrun
=== <Result of SELECT Command in Line 2> ===
host_year nation_code
===================================
2008 'AUS'
1 rows selected.
- 트랜잭션 T1은 participant2 클래스에 또 하나의 인스턴스를 삽입한다. 트랜잭션 T2가 participant2 클래스에 질의했을 때는 커밋된 첫 번째 인스턴스만 보이게 되는데, 그 이유는 이 예제에서 두 번째 인스턴스가 데이터베이스로 출력되지 않았기 때문이다.
- User1 (T1):
- insert into participant2 (host_year, nation_code) values (2004, ‘FRA’);
;xrun
1 rows inserted.
- User2 (T2):
- select * from participant2;
;xrun
=== <Result of SELECT Command in Line 1> ===
host_year nation_code
===================================
2008 'AUS'
1 rows selected.
1 command(s) successfully processed.
- 트랜잭션 T1은 participant2 클래스를 갱신하고 gold 속성을 추가한다. 이 때 트랜잭션 T2가 이 클래스에 질의를 하면 대기 상태가 되는데, 이는 ALTER 문 때문이다. 이는 다른 격리 수준에서도 가능한데, 그 이유는 트랜잭션의 끝까지 클래스에 대한 잠금을 유지하기 때문이다.
- User1 (T1):
- alter class participant2
add attribute gold integer;
;xrun
1 command(s) successfully processed.
- User2 (T2):
- select * from participant2;
;xrun
- 트랜잭션 T1은 participant2 클래스에 gold 속성을 포함한 또 하나의 인스턴스를 삽입한다. 트랜잭션 T1이 연산을 커밋하고 트랜잭션 T2가 다시 진행된다. 트랜잭션 T2는 완전히 다른 스키마(클래스)를 보게 됨을 주의해야 한다.
- User1 (T1):
- insert into participant2 (host_year, nation_code, gold)
values (2012, ‘KOR’, 20);
commit work;
;xrun
1 command(s) successfully processed.
- User2 (T2):
- === <Result of SELECT Command in Line 1> ===
host_year nation_code gold
======================================
2008 'AUS' NULL
2004 'FRA' NULL
2012 'KOR' 20
3 rows selected.