목차

개요

1. READ UNCOMMITTED

2. READ COMMITTED

3. REPEATABLE READ

4. SERIALIZABLE

5. READ ONLY

개요

  • ANSI/ISO SQL 표준은 트랜잭션 시나리오가 같은데도 결과가 다를 수 있는 4 가지 트랜잭션 고립 수준을 정의하고 있음.
  • 주어진 고립 수준에서 허용되거나 허용되지 않는 세 가지 현상(phenomena)으로 고립 수준을 정의함.
구분설명
Dirty read커밋되지 않은, Dirty한 데이터 읽기가 허용됨.
데이터 무결성은 깨지고, 외부키 제약은 위배되고, 유일성 제약은 무시됨.
Non-Repeatable read시간 T1에서 읽은 로우를 시간 T2에서 다시 읽고자 할 때 로우가 바뀌었거나, 사라졌거나 또는 수정되어 있는 현상.
Phantom read시간 T1에서 쿼리를 실행하고 시간 T2에서 쿼리를 재 실행했는데, 그 사이 T1에 없던 로우가 데이터베이스에 추가되어 결과가 달라질 수 있음.
이미 읽은 데이터가 변경된 것이 아니라, 쿼리 조건을 만족하는 데이터가 전보다 많아졌음을 의미하는 것으로 'Non-Repeatable read'와 다름.
  • SQL 고립 수준은 위에서 설명한 각 현상을 허용할지 말지에 대해 정의한 내용
  • ANSI 고립 수준
고립수준Dirty ReadNon-Repeatable ReadPhantom Read
READ UNCOMMITTED허용허용허용
READ COMMITTED--허용허용
REPEATABLE READ----허용
SERIALIZABLE------

1. READ UNCOMMITTED

  • 'READ UNCOMMITTED'는 고립 수준에서 dirty 읽기가 허용되는 것을 의미하며, 오라클은 dirty 읽기를 허용하지는 않음.
  • dirty 읽기는 non-blocking 읽기를 제공하라는 표준 기반의 정의인데, 오라클은 이 차원에서라면 non-blocking 읽기와, 읽기 일관성을 지원함.
  • dirty 읽기를 하지 않고 읽기 일관성을 위해, 오라클은 dirty 블록(DML 되고 COMMIT은 되지 않은) 액세스 시, 블록을 복제한 언두 세그먼트를 통해 읽기 일관성을 처리함.
  • 정리하자면, dirty 읽기는 백해무익하며, 오라클은 dirty 읽기 중 장점인 non-blocking 읽기만 취함.

2. READ COMMITTED

  • 'READ COMMITTED'는 오라클에서 사용하는 기본 모드.
  • 오라클 이외에 타 데이터베이스도 이 모드를 지원하나, 가장 큰 차이난 TX 락 발생 시, 데이터를 읽을 때도 Lock이 걸려 대기를 해야 하며, 이로 인해 오라클과 결과값 차이가 남.
  • 아래는 타 데이터베이스에서 처리하는 방식

<READ COMMITTED 고립을 사용하는 비오라클 데이터베이스 타임라인>

시간쿼리Account 이체 트랜잭션
T1로우 1을 읽는다.
account는 123, 값은 $500, 지금까지의 합계는 $500.00 이다.
--
T2로우 2을 읽는다.
account는 456, 값은 $240.25, 지금까지의 합계는 $740.25 이다.
--
T3--로우 1을 수정하고 다른 수정이나 읽기를 막기 위해 배타 락을 건다.
로우 1은 $500.00에서 $100.00으로 수정되었다.
T4로우 N을 읽는다.
지금까지의 합계는...
T5--로우 342,023(account 987)을 수정하고 배타 락을 건다.
로우 342,023은 $100에서 $500으로 수정되었다.
T6로우 342,023(account 987)을 읽으려고 하지만, 락이 걸려 있다는 것을 발견한다.
이 세션은 블로킹된 채로 이 로우를 포함하고 있는 블록을 사용할 수 있을 때까지 기다린다.
(쿼리에 대한 처리는 중지된다)
--
T7--트랜잭션을 커밋한다.
T8로우 342,023(account 987)을 읽고, $500.00을 더한다.
결국 $400.00을 두 번 더한 꼴이 된다.
--
  • 이 방식의 가장 큰 단점은 아래와 같음.
    • 데이터 쿼리 시, 다른 유저가 액세스 하는 데이터를 DML 할 경우 결과가 다르게 나올 수 있음.
    • 또한, 잘못된 결과를 보기 위해 많은 시간을 대기할 수 있음.
  • 오라클은 위와같이 DML 데이터에를 읽을 때, Lock이 발생하지 않으므로 일관된 결과를 얻을 수 있다.

3. REPEATABLE READ

  • 'REPEATABLE READ'의 목적은 일관성 있고 정확한 결과를 내면서도, lost update를 방지하는 고립 수준을 제공하는 것.

3-1) 일관성 있는 결과 얻기

  • 'REPEATABLE READ' 고립 수준의 데이터베이스에서 실행되는 쿼리는 어느 한 시점을 기준으로 일관성있는 결과를 내야 함.
  • 이를 위해, 'READ COMMITTED'에서는 쿼리하는 세션이 트랜잭션 처리하는 세션에 의해 Lock이 걸리지만, 'REPEATABLE READ'에서는 쿼리하는 세션이 Lock을 걸고 트랜잭션 세션이 대기를 함.
  • 오라클을 제외한 대부분의 데이터베이스는 로우 단위의 공유 읽기 락을 사용해서 'REPEATABLE READ'을 구현함.

<REPEATABLE READ 고립을 사용하는 비 오라클 데이터베이스 타임라인 #1>

시간쿼리Account 이체 트랜잭션
T1로우 1을 읽는다.
합계는 $500.00, 블록 1은 로우 1에 공유 락을 갖고 있다.
--
T2로우 2을 읽는다.
지금까지의 합계는 $740.25, 블록 2은 로우 2에 공유 락을 갖고 있다.
--
T3--로우 1을 수정하려고 하지만, 블로킹한다.
트랜잭션은 배타 락을 획득할 때까지 중지된다.
T4로우 N을 읽는다.
합계는...
--
T5로우 342,023을 읽는다.
$100을 합계에 더한다.
--
T6트랜잭션을 커밋한다.--
T7--로우 1을 수정하고 블록에 배타 락을 건다.
로우 1은 $100.00으로 수정되었다.
T8--로우 342,023을 수정하고 블록에 배타 락을 건다.
로우 342,023은 $500.00으로 수정되었다.
트랜잭션을 커밋한다.
  • 두 번째 'REPEATABLE READ' 고립 수준의 단점은, 잘못하면 DeadLock과 같은 교착 상태가 발생될 수 있음.

<REPEATABLE READ 고립을 사용하는 비 오라클 데이터베이스 타임라인 #2>

시간쿼리Account 이체 트랜잭션
T1로우 1을 읽는다.
합계는 $500.00, 블록 1은 로우 1에 공유 락을 갖고 있다.
--
T2로우 2을 읽는다.
지금까지의 합계는 $740.25, 블록 2은 로우 2에 공유 락을 갖고 있다.
--
T3--로우 342,023을 수정하고 다른 수정과 공유 읽기를 막기 위해 블록 342,023에 배타 락을 건다.
T4로우 N을 읽는다.
합계는 ...
--
T5--로우 1을 수정하려고 하지만 블로킹된다.
트랜잭션은 배타 락을 획득할 수 있을 때까지 중지된다.
T6로우 342,023을 읽으려고 하지만, 이미 배타 락이 걸려 있어서 읽을 수 없다.--

3-2) Lost Update

  • 공유 읽기 락을 채택한 가장 큰 이유는 'Lost Update'를 막기 위해서임.
  • 그러나 'Lost Update'를 막기 위해 공유 읽기를 허용한다면, 이로 인해 읽기와 변경 작업의 동시 진행을 심각하게 저해할 것이므로, 더 큰 손실을 볼 수 있음.

4. SERIALIZABLE

  • 'SERIALIZABLE' 고립 수준은, 가장 엄격한 트랜잭션 고립 수준이며, 가장 높은 수준의 고립성을 제공함.
  • 데이터를 변경하는 다른 사용자가 전혀 없는 데이터베이스 환경에서 작업하는 것처럼 보이도록 트랜잭션을 처리함.
  • 오라클은 직렬화에 대해 낙관적인 접근 방법을 취하는데, 트랜잭션이 UPDATE하기 원하는 데이터를 다른 트랜잭션이 UPDATE 하는 일은 발생하지 않을 것이라 가정함.
  • 하지만 가끔 아래와 같은 에러가 나오기도 함

--ERROR at line 1:
--ORA-08177: can't serialize access for this transaction

  • 하지만 위와 같은 에러는 'SELECT FOR UPDATE' 구문을 사용하여 액세스를 직렬화 하는 것이 좋음.
  • 만약, 'SERIALIZABLE' 고립 수준으로 사용하려면 아래와 같은 상황에서 사용하는 것이 좋음.

1. 누군가 같은 데이터를 변경할 가능성이 매우 낮을 때
2. 트랜잭션 수준의 읽기 일관성이 필요할 때
3. 트랜잭션 수행 시간이 짧을 때(위 첫 번째 항목을 실현하는 데 도움이 될 만큼)

  • 'SERIALIZABLE'에 대한 이해를 돕기 위해 아래의 테스트 Case를 살펴봄

<READ COMMITTED 모드>


-- 1. (Session_1) 샘플 데이터 생성
CREATE TABLE A (X INT);
CREATE TABLE B (X INT);

-- 2. (Session_1) 데이터 생성
INSERT INTO A
SELECT COUNT(*)
FROM   B
;

COMMIT
;

SELECT *
FROM   A
;

         X       
----------       
         0       

1 row selected.
;

-- 3. (Session_2) 데이터 생성
INSERT INTO B
SELECT COUNT(*)
FROM   A
;

COMMIT
;

SELECT *
FROM   B
;

         X     
----------     
         1     

1 row selected.
;

<SERIALIZABLE 모드>

  • 테스트 실패! ㅡ_ㅡ; 스터디 시간에 같이 논의해 봄!

-- 1. (Session_1) 샘플 데이터 생성
DROP TABLE A;
DROP TABLE B;

CREATE TABLE A (X INT);
CREATE TABLE B (X INT);

-- 2. (Session_1) 세션 변경
ALTER SESSION SET ISOLATION_LEVEL=SERIALIZABLE;

-- 3. (Session_1) 데이터 생성
INSERT INTO A
SELECT COUNT(*)
FROM   B
;

COMMIT
;

SELECT *
FROM   A
;

         X       
----------       
         0       

1 row selected.
;

-- 4. (Session_2) 세션 변경
ALTER SESSION SET ISOLATION_LEVEL=SERIALIZABLE;

-- 5. (Session_2) 데이터 생성
INSERT INTO B
SELECT COUNT(*)
FROM   A
;

COMMIT
;

SELECT *
FROM   B
;

         X     
----------     
         1     

1 row selected.
;

5. READ ONLY

  • 'READ ONLY' 트랜잭션은 변경을 허용하지 않는다는 것 빼고는 'SERIALIZABLE' 트랜잭션과 매우 유사해서 'ORA-08177' 오류의 영향을 받지 않음.
  • 레포트 내용이 어느 한 시점을 기준으로 일관적이여야 하는 요구사항을 지원하기 위해 'READ ONLY' 트랜잭션이 만들어짐.
  • 오라클에서도 이 트랜잭션을 사용할 수 있는데, 문제는 'ORA-08177' 오류는 피하지만, 'ORA-01555 snapshot too old' 오류를 만날 수 있음.
    이를 피하기 위해서는 언두 세그먼트의 크기를 조정해야 함.

문서에 대하여