Transaction Isolation

na.ram·2024년 9월 12일

Spring

목록 보기
5/13
post-thumbnail

A 서비스에서 A 레포지토리 외에 다른 레포지토리를 주입받아서 단순 조회를 한다면 상관없지만, 그 외에 값에 대한 어떠한 변경 작업이 일어난다면 A 서비스는 B 레포지토리를 주입받는 것이 아니라 B 서비스를 주입받아야 한다.

그러나 이때 A 서비스의 트랜잭션이 부모가 되어 B 서비스의 트랜잭션이 병합되기 때문에 생각한 것과는 다르게 동작할 수 있으므로 트랜잭션을 격리해주어야 할 수도 있다.

그렇다면 트랜잭션 격리란 무엇이고, 어떻게 하는걸까?

Transaction Isolation

Isolation은 일반적인 ACID 특성(원자성, 일관성, 고립성, 영속성) 중 하나이며, 트랜잭션 격리 수준이란 동시에 여러 트랜잭션이 처리될 때, 트랜잭션끼리 서로 얼마나 고립되어 있는지를 나타내는 것이다.
간단하게는 특정 트랜잭션이 다른 트랜잭션에서 변경한 데이터를 볼 수 있도록 허용할지 말지를 결정하는 것이다.

각 격리 수준은 트랜잭션에 대한 0 또는 그 이상의 동시성 사이드 이펙트를 방지한다.

  • Dirty Read : 한 트랜잭션에서 다른 트랜잭션의 커밋되지 않은 변경 내용을 읽는다.
  • Nonrepeatable Read : A 트랜잭션에서 데이터를 읽은 후에 B 트랜잭션이 데이터를 변경하면 A 트랜잭션이 데이터를 다시 읽을 때 최초에 읽은 값과는 다른 값을 얻는다.
  • Phantom Read : 한 트랜잭션에서 데이터를 읽은 후에 다른 트랜잭션이 일부 행을 추가하거나 제거하고 커밋하면 최초에는 없던 값(Phantom)을 읽는다.

우리는 @Transactional::isolation으로 트랜잭션의 격리 수준을 설정할 수 있으며, Spring에서는 DEFAULT, READ_UNCOMMITED, READ_COMMITTED, REPEATABLE_READ, SERIALIZEABLE와 같은 타입으로 정의한다.

DEFAULT

  • RDBMS에 설정한 기본 격리 수준을 따라간다.

READ_UNCOMMITED

  • 가장 낮은 격리 수준으로 동시 접근을 허용한다.
  • 이 레벨에서는 다른 트랜잭션의 커밋되지 않은 데이터를 읽을 수 있다.
  • Dirty Read, Nonrepeatable Read, Phantom Read 모두 방지할 수 없다.
  • Postgres는 READ_UNCOMMITTED을 지원하지 않으며 대신 READ_COMMITED로 돌아간다. 또한, Oracle도 READ_UNCOMMITTED을 지원하거나 허용하지 않는다.

READ_COMMITED

  • 두 번째 격리 수준으로 Dirty Read를 방지한다.
  • 다른 트랜잭션의 커밋되지 않은 변경은 영향을 미치지 않지만, 커밋된 변경에 의해서는 결과가 바뀔 수 있다.
    • SELECT 문장이 수행되는 동안 해당 데이터에 Shared Lock(읽기 가능, 변경 불가)이 걸리는 레벨
  • Nonrepeatable Read, Phantom Read 모두 방지할 수 없다.
  • Postgres, SQL Server 및 Oracle의 기본 격리 수준이다.

REPEATABLE_READ

  • 세 번째 격리 수준으로 Dirty Read, Nonrepeatable Read를 방지한다.
  • lost update를 방지하기 위해 필요한 가장 낮은 수준이다. lost update는 두 개 이상의 동시 트랜잭션이 같은 행을 읽고 수정할 때 발생하는데,REPEATABLE_READ는 한 행에 대한 동시 접근을 허용하지 않기 때문에 loat update가 발생할 수 없다.
  • REPEATABLE READ는 MVCC를 이용해 한 트랜잭션 내에서 동일한 결과를 보장하지만, 새로운 레코드가 추가되는 경우에 부정합이 생길 수 있다.
  • Phantom Read를 방지할 수 없다.
  • MySQL의 기본 격리 수준이며, Oracle은 REPEATABLE_READ를 지원하지 않는다.

SERIALIZEABLE

  • 가장 높은 격리 수준으로 모든 동시성 사이드 이펙트를 방지한다.
  • 모든 트랜잭션을 순서대로 실행한다. 때문에 가장 낮은 동시 접속 속도를 초래할 수 있다.
  • Dirty Read, Nonrepeatable Read, Phantom Read 모두 방지할 수 있다.
  • 극단적으로 안전한 작업이 필요한 경우가 아니라면 성능 문제로 인해 사용을 지양한다.

정리

SERIALIZABLE를 제외하고는 크게 성능 개선 및 저하가 발생하지 않는다. 그 이유는 결국 Undo 로그를 통해 레코드를 참조하는 과정이 거의 동일하기 때문이다.

참고

Transaction Propagation and Isolation in Spring @Transactional
[MySQL] 트랜잭션의 격리 수준(Isolation Level)에 대해 쉽고 완벽하게 이해하기

0개의 댓글