Transaction Isolation Levels in Oracle

개요

개발을 하다보면 동시성과 일관성에 대해서 한번쯤 생각하게된다.
동시성을 높이면 일관성이 떨어지고, 일관성을 높이면 동시성이 떨어진다.. 그러면 어느정도의 레벨로 맞춰야할까 언제 동시성을 높이고 언제 일관성을 높여야 할까??
이런생각 중에 오라클에서는 동시성 제어와 읽기 일관성을 유지를 어떻게 하는지 궁금해져서 오라클 문서와 블로그등을 참고해서 정리해보았다
동작원리를 이해하기 위해선 먼저 Transaction Isolation Levels을 알아야한다.

Transaction Isolation Levels

ANSI/ISO 표준에서 Transaction Isolation Levels을 다음 4가지로 정의하고있다.

  • READ UNCOMMITTED
  • READ COMMITTED
  • REPEATABLE READ
  • SERIALIZABLE

1. READ UNCOMMITTED

다른 트랜잭션의 변경내용이 커밋하지 않아도 보여진다.
예를 들어 다음과 같은 Account 테이블이 있다고 가정하자

row id balance
1 123 10000
2 222 30000
3 333 50000

[table1. Account 테이블]

그리고 다음과 같은 시나리오를 생각해보자.

time transaction A transaction B
T1 select balance from Account where id = ‘123’ -
T2 - update Account set balance = balance + 10000 where id = ‘123’
T3 select balance from Account where id = ‘123’ -
T4 - commit;

[table2. 트랜잭션 시나리오 1]

A 트랜잭션이 실행되는 중간에 다른 세션에서 update를 하고 커밋을 하지 않았다.
transaction A의 T1에서 조회한 값과 T3에서 조회한 balance값은 어떻게 나올까?
결과는 T1 = 10000, T3 = 20000 이다.
이렇게 실시간으로 다른 트랜잭션의 커밋되지않은 변경이 보여지는 현상을 Dirty read라고 한다.
동시성이 좋지만 일관성을 전혀 보장하지 못한다.

2. READ COMMITTED

다른 트랜잭션의 변경내용이 커밋되어야 보여진다.
READ COMMITTED 레벨에서 [table2. 트랜잭션 시나리오 1]이 시행되면 똑같은 T1 = 10000, T3 = 10000 값을 얻을 수있다. 커밋을 하지 않았기 때문이다.
하지만 B 트랜잭션에서 커밋을하게되면 역시 바뀐값을 읽게된다.
이러한 현상을 Nonrepeatable read라한다. 하나의 트랜잭션안에서는 같은 쿼리를 수행했을 때 항상 같은 값을 가져야한다는 read consistent에 부합되는 현상이다.

3. REPEATABLE READ

REPEATABLE READ 레벨에선 Dirty read와 Nonrepeatable read가 없다.
커밋을해도 T1,T3결과가 모두 10000으로 조회되어 read consistent를 보장한다.
하지만 Phantom READ를 허용한다. Phantom READ의 예제 시나리오는 다음과 같다.

time transaction A transaction B
T1 select count(*) from Account -
T2 - insert into Account values (4, 1000)
commit;
T3 select count(*) from Account -

[table3. 트랜잭션 시나리오 2]

T1에서의 결과보다 T3의 조회한 카운트가 하나더 증가해서 나온다. 이런현상을 Phantom READ라한다

유령(Phantom)이 나타났다 ㄷㄷㄷ

4. SERIALIZABLE

가장 강한 격리 레벨로 정합성을 보장하지만 하나의 트랜잭션의 끝날때까지 다른트랜잭션이 접근을 못해서 성능 저하가 발생할 수 있다. Dead Lock 가능성도 있음

다음은 isolation levels permit 요약표다

Isolation Level Dirty Read Nonrepeatable Read Phantom Read
READ UNCOMMITTED Permitted Permitted Permitted
READ COMMITTED Permitted Permitted
REPEATABLE READ Permitted
SERIALIZABLE

[table4. isolation levels]

Data Concurrency and Consistency in Oracle

그렇다면 오라클 DBMS에서는 어떤 트랜잭션 격리 레벨을 사용하고 있으며 어떻게 데이터 동시성과 일관성을 보장할까?
오라클은 기본적으로 READ COMMITTED 레벨로 설정되어있으며, 동시성 제어를 위해 MVCC매커니즘을 사용하고있다.

[table2. 트랜잭션 시나리오 1]을 다시보고 오라클 기본설정인 READ COMMITTED에서는 어떻게 동작할 지 생각해보자.
일단 MVCC가 뭔지 모르니 MVCC를 사용하지 않는 DBMS와 어떻게 다르게 동작하는지 알아보자

  1. 일반적인 Lock을 사용한 제어에서 [table2. 트랜잭션 시나리오 1]
time transaction A transaction B
T1 balance = 10000을 읽음 -
T2 - row 하나를 업데이트를 하고 exclusive lock을 건다
T3 exclusive lock이 걸려있어서 block되고 wait함
B transaction이 끝나면 balance = 20000을 읽음
-
T4 - commit;

B에서 update한 순간 lock이걸리고 exclusive lock이기 때문에 T3시간에서는 접근을 못한다.
B에서 commit을 하면 lock이 풀리고 그후에 row를 읽어온다.

  1. MVCC를 사용한 제어에서 [table2. 트랜잭션 시나리오 1]
time transaction A transaction B
T1 해당 로우를 포함한 블록을 복사해서 따로 저장해놓는다
balance = 10000을 읽음
-
T2 - row 하나를 업데이트를 하고 exclusive lock을 건다
T3 복사한 버전에서 balance = 10000을 읽음 -
T4 - commit;

T1에서 A는 쿼리가 시작하는 시점에서 해당 row를 포함한 블록을 새로 만들어 임시 저장해놓는다. 일종의 snapshot이다
그리고 트랜잭션이 끝날때까지 그것만 읽게된다.
T2에서 B가 update문을 실행하면서 exclusive lock이 걸리지만 T3에서 A는 복사된 블록을 읽기때문에 B가 완료되지 않아도 접근이 가능한 것이다. T4에서 B가 commit하면 업데이트된 값으로 새로운 버전의 블록을 생성한다.
이렇게 하나의 데이터에 대해 여러 버전의 데이터가 존재하는 구조로 만드는 방식을 MVCC(Multi-Version Concurrency Control)라고한다.

너무 어렵다…ㅠㅠ 이해를 돕기위해 예시를 들어보자

time transaction A transaction B
T1 select balance from account where id=’123’ -
T2 - update account set balance=balance+20000 where id=’123’
T3 update account set balance=balance+10000 where id=’123’ -
T4 - commit;
T5 commit; -

[table5. 트랜잭션 시나리오 3]

이쯤에서 퀴즈~~ ^^ 위의 시나리오에서 최종 업데이트된 balance 값은??

혼란20000? 30000?? 40000???

차례대로 천천히 살펴보자
T1 - 트랜잭션이 시작되고 CR Block(복사본)을 만든다
T2 - id=’123’ row를 업데이트하고 exclusive lock을건다
T3 - update문은 read component(where절) + write component(set절)로 나뉘는데..
read는 CR Block에서 가져오기때문에 처음 시작 때 id=’123’을 선택한다. 하지만 update set절은 write이기 때문에 exclusive lock에 걸리게된다..
간단히 말해 DML은 MVCC적용이 안된다. 고로 대기상태가 됨
T4 - B가 커밋 후에 A는 update를 수행한다. 이 때 balance+10000의 balance는 업데이트된 멀티비전을 읽으므로 30000 + 10000 결과는 40000이된다.

READ ONLY

오라클에는 추가적인 transaction isolation level인 READ ONLY를 제공한다.
일관성이 더 높은 레벨이다. ANSI표준으로 치면 REPEATABLE READ와 같고, SERIALIZABLE에서 수정만 금지한 레벨이다
예약시스템같은 엄격하게 동시성을 제어하고 싶을 때 사용할 수 있겠다. statement에서 사용하려면 select … for update 구문으로 쓰면된다.

3줄요약

  • ANSI/ISO Standard Transaction Isolation Levels은 4가지가 있다 (READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE)
  • 오라클은 read는 MVCC, write는 Lock으로 제어하여 READ COMMITTED Level을 구현한다
  • READ ONLY, SERIALIZABLE은 신중히 사용하자

이해했어

참조