Isolation Level의 필요성
-
DB는 무결성을 보장하는 것이 중요하다.
그리고 그 무결성을 보장하기 위한 특징이 ACID(Atomicity, Consistency, Isolation, Durability)이다.
DB는 ACID가 의미하는 바와 같이 Transaction이 원자적이면서도 독립적인 수행을 하도록해야 한다.
그래서 등장하는 개념이 Locking이다.
-
Locking은 Transaction이 DB를 다루는 동안 다른 Transaction이 관여하지 못하게 막는다.
하지만 무조건적인 Locking으로
동시에 수행되는 많은 Transaction들을 일렬로 대기시킨다면 DB의 성능은 현저하게 떨어진다.
-
반대로 응답성을 높이기 위해
Locking 범위를 줄인다면 잘못된 값이 처리 될 여지가 있다.
그래서 최대한 효율적인 Locking 방법이 필요하다.
이와 관련된 Locking 방법이 Isolation Level이다.
Isolation Level의 종류
Read Uncommitted
-
Select 문장을 수행하는 경우 해당 데이터에 Shared Lock이 걸리지 않는 Level이다.
-
따라서 어떤 사용자가 A -> B로 데이터를 변경하는 동안
다른 사용자는 완료되지 않은(Uncommitted 혹은 Dirty Data) B를 읽을 수 있다.
-
즉 Transaction이 끝나지 않은 상황에서
각기 다른 Transaction이 변경한 내용에 대한 조회가 가능하다.
그렇게 되면 데이터베이서의 일관성을 유지할 수 없다.
-
Transaction1이 최초 수행되고
그 뒤에 Transaction2가 값을 변경할 경우
다시 Transaction1이 조회를 하게 되면
Transaction2가 Commit은 하지 않았지만
이미 Transaction2가 값을 변경하였기 때문에
아무개 에서 개발자 로 값이 변경되어 조회가 된다.
Read Committed
-
SQL Server가 Default로 사용하는 Isolation Level이다.
-
이 Level에선 Select 문장이 수행되는 동안 해당 데이터에 Shared Lock이 걸린다.
-
그러므로 어떠한 사용자가 A -> B로 데이터를 변경하는 동안
다른 사용자는 해당 데이터에 접근할 수 없다.
-
Read Uncommitted와 다르게 Commit이 이루어진 데이터가 조회된다.
-
하지만 어떠한 사용자가 A -> B로 데이터를 변경하는 동안
다른 Transaction은 접근할 수 없어 대기하게 된다.
-
Transaction2가 Update를 하게된다.
-
아직 Commit하지 않아 Transaction1은 Select을 하지 못하고 대기하게 된다.
-
Transaction2가 Commit명령어를 날리게 된다.
-
이제 Transaction2는 조회가 가능하다.
Repeatable Read
-
Transaction이 완료될 때까지
Select 문장이 사용하는 모든 데이터에 Shared Lock이 걸리므로
다른 사용자는 그 영역에 해당되는 데이터에 대한 수정이 불가능하다.
-
가령 Select col1 from A Where col1 between 1 and 10 을 수행하였고
이 범위에 해당하는 데이터가 2건이 있는 경우 (col1 = 1,5)
다른 사용자가 col1 = 1 혹은 col1 = 5인 Row에 대한 Update 작업이 불가능하다.
하지만 col1이 1과 5를 제외한
나머지 범위에 해당하는 Row를 Insert하는 것은 가능하다.
-
그 결과 Transaction이 최초 수행된 후
해당 범위내에서는 조회한 데이터의 내용이 항상 동일함을 보장한다.
-
Transaction1이 Select시점에 아무개가 조회된다.
-
Transaction2가 Update후 Commit을 시행하였지만 Update가 안된다.
그러나 Insert는 된다. -
Transaction1이 다시 조회 해도 Transaction2가 Commit이 되지 않았기 때문에 아무개로 조회된다.
하지만 Insert한 동네개발자는 조회된다. -
Transaction1이 종료되면 다시 Commit이 이루어지기 때문에 개발자로 조회가 된다.
Serializable
-
Index가 설정되어 있지 않다는 조건하에
모든 동작이 직렬화되어 동작한다.
-
그러므로 Repeatable Read와 다르게
Insert를 하여도 동작하지 않게 된다.
-
Transaction이 완료될 때까지
Select 문장이 사용하는 모든 데이터에 Shared Lock이 걸리므로
다른 사용자는 그 영역에 해당하는 데이터에 대한 Update 및 Insert가 불가능하다.
-
기본적으로는 위 개념처럼 동작하지만
Index가 설정되어 있다면 조금 다르게 동작한다.
Index 설정 O
-
Table에 총 1~10 sequence가 있고
A Transaction이
select * from table_name where 3 <= seq <= 5 를 조회하는 상황에서
B Transaction이 데이터를 insert를 하려고 하면
Index 값을 보고 Success / Fail을 하게 된다.
Insert seq = 1~2 ==> Success
Insert seq = 3~5 ==> Fail
Insert seq = 6~10 ==> Success
Index 설정 X
-
Index가 설정되어 있지 않으면
기본적으로 모든 데이터에 대해 Lock을 걸어버린다.
그래서 모든 Insert 요청이 실패한다.
Insert seq = 1~2 ==> Fail
Insert seq = 3~5 ==> Fail
Insert seq = 6~10 ==> Fail
부작용(Side Effect)
Dirty Read
-
A Transaction 입장에서
아직 실행이 끝난지 않은 B Transaction에 의한 변경 사항을 보게되는 경우를 Dirty Read라고 한다.
-
만약 수정한 Transaction이 그 변경 사항을 롤백하면
그 데이터를 읽은 다른 Transaction은 Dirty 데이터를 가지고 있다고 말한다.
Non-Repeatable Read
-
A Transaction에서 같은 질의를 여러번 하여도
B Transaction에서 변경한 사항을 반영하지 못하고
변경되기전의 같은 데이터만 읽어드리는 경우를 Non-Repeatable Read라고 한다.
-
즉 Non-Repeatable Read Level을 사용하는 DB에서는
다른 Transaction에 의한 변경 사항을 볼 수가 없다.
변경 사항을 보고 싶다면 Application에서 Transaction을 새로 시작해야 한다.
Phantom Read
-
Phantom Read는 다른 Transaction에 의한 변경 사항으로 인해
현재 사용 중인 Transaction의 Where 절의 조건에 맞는 새로운 행이 생길 수 있는 경우에 관한 것이다.
-
예를 들어 잔고가 $100 미만인 계좌가 2개인 DB에서
$100 미만인 계좌를 찾는 Transaction이 있고
그 Transaction안에서 Select 쿼리를 2번 수행한다고 가정하자.
처음에 데이터를 읽으면 2개의 계좌를 찾게 된다.
이 때 다른 Transaction에서 $0인 계좌를 새로 만들면
두번째 데이터를 읽을 땐 3개의 계좌를 찾게 된다.
-
이처럼 Where 절의 조건에 맞는 새로운 행이 생길 수 있는 경우를 말한다.
-
DB의 Transaction Isolation Level에서 Phantom Read를 지원하면
새로운 유령(phantom)행이 나오지만 지원하지 않으면 새로 생긴 행을 볼 수 없다.
추가 정보
-
Oracle Default Isolation Level : READ-COMMITED
MySQL InnoDB 스토리지 엔진의 Default Isolation Level : REPEATABLE-READ
-
Oracle은 READ-COMMITED와 SERIALIZABLE 만 지원하며
나머지 두가지 Isolation Level은 지원하지 않는다.
-
각 DBMS별 isolation Level 에 자세한 내용은 다음 링크에서 참조할 수 있다.