온라인강의

[udemy] 데이터 베이스 엔지니어링 - Isolation

콩콩(๓° ˘ °๓)♡ 2024. 6. 30. 08:51

 

Isolation 고립성

고립성은 동시에 발생하는 다른 트랜잭션과 완전히 고립된 상태에서 트랜잭션이 실행되는 결과입니다. 

현재 진행 중인 내 트랜잭션이 다른 진행 중인 트랜잭션에서 발생한 변경 사항을 볼 수 있을까요?

정답는 상황에 따라 다르다! 입니다.

그래서 다양한 읽기 현상에 대해서 알아야 하는데요,  디버깅하기 어렵게 만드는 읽기 현상들을 해결하기 위한 고립수준까지 이어서 알아보겠습니다.

 

Read Phenomena 읽기 현상들

1. Dirty reads 더티 읽기

다른 트랜잭션이 쓴 내용을 읽지만 실제로는 아직 커밋되지 않은 것을 읽는 현상

트랜잭션 1 트랜잭션 2
상품 1의 판매량 15를 읽어옴  
  상품 1의 판매량에 5를 더해 20을 만듦
판매량 20에 가격 10을 곱해 매출을 계산  
커밋 롤백

=> 원래 매출 집계로 150을 얻어야 하지만 200을 얻음

 

2. Non-reapeatable reads 중복되지 않는 읽기

트랜잭션 중에 값을 읽은 후, 동일한 트랜잭션에서 다시 그 값을 읽는 사이에 값이 변경되는 현상

트랜잭션 1 트랜잭션 2
상품 1의 판매량 15를 읽어옴  
  상품 1의 판매량에 5를 더해 20을 만듦
  커밋
판매량 20에 가격 10을 곱해 매출을 계산  
커밋  

=> 원래 매출 집계로 150을 얻어야 하지만 200을 얻음

 

3. Phantom reads 유령 읽기

범위쿼리로 읽어온 데이터에 새 행을 추가한 뒤 다시 select를 했을 때 추가한 유령행이 읽히는 현상

트랜잭션 1 트랜잭션 2
판매량 10이상 20이하의 상품의 매출 조회  
  새로운 상품 3의 판매량 15, 판매가 10$ 행 추가
  커밋
해당 범위의 상품의 매출액 집계 (판매량 *판매가)  
커밋  

=> 읽지않은 행을 lock 할 수 없어 문제가 됨다

 

4. Lost updates 잃어버린 업데이트

쓰기와 커밋 사이에 다른 트랜잭션이 해당 값을 삭제해서 바로 읽기를 실행해도 찾지 못하는 현상

트랜잭션 1 트랜잭션 2
상품 1의 판매량 15를 읽어옴 상품 1의 판매량 15를 읽어옴
판매량에 10을 더해 25를 만듦 판매량에 5를 더해 20을 만듦
  커밋
판매량과 판매가를 읽어와  곱해 매출을 집계함  
커밋  

=> 트랜잭션 2가 먼저 커밋한 판매량 20을 읽어와 실제보다 작은 결과값을 얻음

 

Isolation Levels for inflight transactions 고립수준

읽기현상을 해결하기 위함으로 트랜잭션을 시작하기 전 set isolation level 명령어로 고립수준을 설정합니다.

 

1. Read Uncommitted

다른사람이 수정하는 모든 커밋되지 않는 변경을 읽음. 더티읽기 발생 가능. 하지만 빠르다.

 

2. Read committed

트랜잭션의 각 읽기는 커밋된 변경만 읽을 수 있다. 일관성이 없을 수 있음. 많은 데이터베이스의 기본 설정값.

 

3. Repeatable Read

한 트랜잭션이 실행되는 동안 읽은 값을 메모리에 복사해둠으로써 해당 트랜잭션 내에서는 값이 변경되지 않음

 

4. Snap shot

각 쿼리는 트랜잭션이 시작하기 직전에 커밋된 변경사항 까지만 읽을 수 있다. 유령읽기를 제외한 모든 읽기 현상을 제거한다. Postgre의 기본 설정. 트랜잭션 시작 시점을 타임스탬프로 보관해 버전으로 관리한다. 

 

5. Serializable 직렬화

가장 느리나 동시성이 없음을 보장한다. 데이터베이스가 먼저 진행되는 순서를 결정하여 거의 동일한 결과를 얻게 한다. 선형화 가능성의 문제를 내포함. 

 

Database Implementation of Isolation 고립성의 구현

각 DBMS는 서로 다른 고립 수준을 구현하고 있습니다.

 

1. 비관적 방식

 

비관적 동시성 제어. 행 레벨 잠금, 테이블 잠금, 페이지 잠금(클러스터링)으로 한 트랜잭션이 업데이트를 수행할 때 잠금 lock을 사용해 다른 트랜잭션의 접근을 막는다. 잠금 상승 lock escalation으로 모든 트랜잭션이 대기하는 상황이 발생할 수 있다. 행 레벨 잠금의 경우 행의 개수만큼 잠금이 필요하는 등 비용이 많이 듦.

 

2. 낙관적 방식

 

실제로 잠금을 하지 않고 상황에 맞게 처리하다가, 트랜잭션이 충돌하면 트랜잭션을 실패시키고 다시 시도한다(직렬화 오류). NoSQL계열이 선호하는 방식.

 

3. 반복 가능한 읽기 잠금

 

postgre가 사용하는 snap shot방식으로 유령읽기가 발생하지 않는다. 

 

4. 동시성 제어

 

느리지만, 하나씩 수행하게 함으로써 적극적인 동시성 제어로 구현된다. SELECT FOR UPDATE를 사용해 비관적으로 구현할 수 도 있다.