DB Isolation Level (트랜잭션 격리 수준)

bagt13·2022년 10월 23일
0

Database

목록 보기
4/8
post-thumbnail

Isolation level (트랜잭션 격리 수준)

동시에 여러 트랜잭션이 수행될 때, 트랜잭션 간에 고립되어 있는 정도를 나타내는 것이다.

-> '다른 트랜잭션의 변경사항을 읽을 수 있는 정도'를 뜻한다.


Isolation level은 ACID 원칙 중에 격리성과 관련된 기능이다.

트랜잭션간에 격리성을 완벽히 보장하려면 동시에 처리되는 트랜잭션을 거의 차례대로 실행을 해야 하는데, 그렇게 되면 성능이 나빠지게 된다.

그래서 ANSI 표준은 트랜잭션의 격리 수준을 4단계로 나누어 정의하고 있다.

  • READ Uncommitted
  • READ Committed
  • Repeatable READ
  • Serializable

단어에서 유추할 수 있듯이, Read uncommitted는 다른 트랜잭션에서 commit 되지 않는 변경사항까지 읽을 수 있는 것이며, Serializable의 고립성이 제일 높다.

하지만 'Dirty Read'라고도 하는 'Read uncommitted'는 일반적인 데이터베이스에서는 거의 사용하지 않고, Serializable 역시 동시성이 중요한 데이터베이스에서는 거의 사용되지 않는다.

일반적으로는 Read Committed / Repeatable Read를 많이 사용한다.

  • Oracle (default) : Read Committed
  • MySQL (default) : Repeatable Read


Read Uncommitted (Dirty Read)

Read Uncommitted는 commit/rollback 되지 않은, 즉 완료되지 않은 트랜잭션 내용을 다른 트랜잭션에서 읽을 수 있도록 한다.

딱 봐도 큰 문제가 생길 것 같다.


Read Committed (UndoLog 접근 조회)

Read Committed는 commit된, 즉 트랜잭션 작업이 끝난 데이터만 다른 트랜잭션에서 읽을 수 있다.

가장 널리 사용되는 격리 수준이며, Read Uncommitted 보다 훨씬 안전하다.


하지만 이 방법도 ❗️NON-REPEATABLE READ 라는 데이터 부정합 문제가 발생할 가능성이 있다.

예를 들어, 다음과 같은 상황에서 문제가 발생한다.

  • 특정 회원의 이름을 반복적으로 조회하는 트랜잭션 A가 수행된다.
  • 트랜잭션 A는 유지된 채, 특정 회원의 이름을 바꾸는 트랜잭션 B가 수행된다.
  • 트랜잭션 B가 끝나면, 이제 트랜잭션 A는 이전의 조회 결과와 다른 조회 결과를 얻게된다.

'동일한 트랜잭션에서 동일한 작업을 수행했을 때 항상 같은 결과를 보장해야 한다' 는 정합성에 위배되는 것이다.

이러한 현상이 만약 정산 작업과 같이 중요한 작업에서 발생하게 된다면 치명적일 것이다.


Repeatable Read

Repeatable Read에서는 Read Committed 에서 발생하는 NON_REPEATABLE READ 부정합이 발생하지 않는다.

즉, 다른 트랜잭션에 의해 레코드가 사라지거나 값이 바뀌는 현상을 방지해준다.

이 레벨에서는 트랜잭션(데이터 조작 트랜잭션) 시작 시 트랜잭션 id를 부여하여 Undo 영역에 백업된 이전 데이터를 이용하여, 같은 트랜잭션(데이터 조회 트랜잭션) 내에서는 같은 내용을 보여줄 수 있도록 하기 때문이다.

Undo Log란?

rollback을 위해 트랜잭션 시작 시 기존 데이터를 복사해두는 장소


하지만, 새로운 값이 삽입되는 Phantom Read 문제가 발생할 수 있다.

❗️ Phantom Read란?

새로운 레코드가 생기거나, 존재하던 레코드가 사라지는 현상

✅ MySQL InnoDB에서는 괜찮다

MySQL의 InnoDB의 경우 repeatable read에서도 phantom read가 발생하지 않는다.

이 레벨에서는 UndoLog를 버전 관리하기 때문이다. 해당 트랜잭션이 끝날때까지 해당 버전의 undolog를 가지고 있다.


Serializable

격리성을 가장 엄격하게 보장하기 때문에, 동시 처리 성능도 다른 격리 수준보다 현저히 떨어진다.

Serializable로 설정하면 읽기 작업도 공유 잠금(읽기 잠금)을 획득해야 하며, 동시에 다른 트랜잭션은 해당 레코드를 변경할 수 없다.

즉, 한 트랜잭션에서 읽고 쓰는 레코드를 다른 트랜잭션에서는 절대 접근할 수 없다.


Serializable에서는 Phantom Read 문제가 발생하지 않는다. 하지만 앞에서 언급한 것 처럼, InnoDB 스토리지 엔진에서는 Repeatable Read에서도 Phantom Read가 발생하지 않기 때문에 Repeatable Read를 사용하면 될 것 같다.



트랜잭션 격리 수준 조회 (MySQL 기준)

SELECT @@GLOBAL.transaction_isolation;

트랜잭션 격리 수준 설정

set SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

또는

UPDATE @@SESSION.transaction_isolation = 'read-committed';

위처럼 scope를 지정할 수 있는데 scope 마다 차이점은 다음과 같다.

  • GLOBAL : 전역 설정
  • SESSION : 현재 세션 적용
  • 생략 : 다음 트랜잭션만 적용

📖 느낀 점

  • 동시성 고려 -> 데이터의 격리 수준을 낮게 설정 -> 데이터의 일관성이 깨질 확률 상승
  • 데이터의 일관성에 집중 -> 격리 수준 높게 설정 -> 데이터에 lock이 걸려 동시성은 성능 저하

애플리케이션의 형태에 따라 SQL/NoSQL 과 같이 데이터베이스 자체를 선택하는 것 뿐만 아니라, 특정 기능 또는 요구 사항에 격리 수준을 알맞게 설정하는 것도 중요하다는 것을 알게 되었다.

이때 동시 접속 성능과 데이터의 정합성 사이의 trade-off를 잘 고려해서 선택하면 될 것 같다.

profile
주니어 백엔드 개발자입니다😄

0개의 댓글