JPA 트랜잭션 격리 수준

윤종석·2025년 11월 12일

CS

목록 보기
11/11

JPA는 1차 캐시를 통해 Repeatable Read 레벨의 트랜잭션 격리 수준을 어플리케이션 레벨에서 제공한다.

이 Repeatable Read가 무엇일까?

아니 트랜잭션 격리 수준이 무엇일까?

트랜잭션 격리 수준은 동시에 실행하는 여러개의 트랜잭션들을 어느 정도로 시간적으로 분리할지를 결정하는 것이다. ACID의 isolation를 속성을 지켜야하지만, 성능을 위해 얼마나 타협할지를 정하는 것이다.

트랜잭션 격리 수준은 다음 4가지 레벨이 있다.

  • Read Uncommitted
  • Read Committed
  • Repeatable Read
  • Serialized

위에서 아래로 갈수록, 성능은 떨어지지만, 발생 가능한 동기화 문제는 줄어든다. Serialized는 이름 그대로 모든 트랜잭션을 직렬화해서 순차적으로 실행한다. 따라서 어떠한 동기화 문제가 전혀 발생하지 않는다.

Serialized를 제외한 세가지를 순차적으로 알아보자.

들어가기에 앞서, 모든 트랜잭션은 commit 혹은 rollback으로 종료된다는 점을 명심하자.

Read Uncommitted

Read Uncomitted 레벨의 트랜잭션은 다른 트랜잭션이 변경했지만, 커밋하지 않은 인스턴스 내용을 조회할 수 있다. 즉, Dirty Read가 가능하다.

Dirty Read는 어떻게 이루어지는가?
RDBMS의 commit은 메모리에만 저장한 변경 내용을 DISK로 넘기면서 영속화하는 과정이다. 즉, RDBMS에 쿼리만 보내고 commit하지 않는다면, 변경 내용은 RDBMS의 메모리에만 저장된다.
RDBMS의 메모리에는 Buffer pool이 있고, 여기에서 Dirty Page와 Redo log, undo log등을 관리한다.
Dirty Page에는 변경됐지만 commit되지 않은 페이지들을 저장하고, Dirty flag와 변경된 값등을 관리하는데, 이 Dirty Page를 읽는 것이 바로 Dirty Read이다.
(정작 커밋할 때는, Redo Log를 메인으로 영속화한다.)

당연히 Non-repeatable Read도 발생하지만, 그건 다음에서 다루겠다.

Read Committed

Read Committed 레벨의 트랜잭션은 자신이 실행 중에 다른 트랜잭션이 commit한 값을 읽을 수 있다.
따라서 트랜잭션이 실행되는 동안 특정값이 예기치 못하게 변경되는 Non-repeatable Read가 발생할 수 있다.

Repeatable Read

Repeatable Read의 트랜잭션은 자신이 시작되는 시점 이전에 커밋된 내용만을 읽는다. 읽는 데이터들의 시점이 고정되므로, 마치 정적인 스냅샷을 읽는 것처럼 동작한다. 이를 통해 Non-repeatable Read까지 예방한다.

이것이 어떻게 가능할까?
마치 트랜잭션 실행 시점에 조회하는 값들을 다 복사하는 것처럼 동작하지만, 실제로는 그렇지 않다. 실행 시점에 별도의 복사가 이루어지지는 않는다.
RDBMS는 Undo Log를 이용해 row 별로 스냅샷을 유지한다. 물론, 오래된 스냅샷들은 내부적으로 삭제하기 때문에, 쿼리를 반복한다고 계속해서 용량이 커지지는 않는다.

RU는 메모리만 조회하면 되고, RC는 디스크만 조회하면 되고, RR은 디스크의 컬럼 + Undo Log까지 조회해야 한다. 부작용을 예방할수록 오버헤드는 증가한다.

innoDB의 기본 격리 수준은 Repeatable Read이지만, 다른 대부분의 RDBMS들은 Read Committed이다. 이를 유의해서 쿼리를 사용하자.

profile
node로 다하고 싶어요

0개의 댓글