트랜잭션 트랜잭션 아이솔레이션 레벨 - 격리 수준(isolation level)

Jobmania·2022년 12월 29일
0

데이터베이스

목록 보기
1/4
post-thumbnail

https://youtu.be/taAp_u83MwA

트랜잭션 격리수준(isolation level)이란 동시에 여러 트랜잭션이 처리될 때, 트랜잭션끼리 얼마나 서로 고립되어 있는지를 나타내는 것이다.
즉, 간단하게 말해 특정 트랜잭션이 다른 트랜잭션에 변경한 데이터를 볼 수 있도록 허용할지 말지를 결정하는 것이다.

데이터베이스 트랜잭션이 안전하게 수행된다는 것을 보장하기 위한 성질 네가지가 존재합니다. (ACID)
원자성(Atomicity): 한 트랜잭션 내에서 실행한 작업들은 하나로 간주함 (모두 성공 또는 모두 실패)
일관성(Consistency): 트랜잭션은 일관성 있는 데이터베이스 상태를 유지해야 함
격리성(Isolation): 동시에 실행되는 트랜잭션들이 서로 영향을 미치지 않아야 함
지속성(Durability): 트랜잭션을 성공적으로 마치면 결과가 항상 저장되어야 함

격리수준은 크게 아래의 4개로 나뉜다.
1. READ UNCOMMITTED
2. READ COMMITTED
3. REPEATABLE READ
4. SERIALIZABLE

각 각의 특징에 대해서 정리하고 DB마다 격리수준이 다르기 때문에 DB선택시 성능적인 부분과 데이터 안정성에 부분을 고려해야한다.

Spring 의 @Transactional 에서는 총 5가지 isolation 옵션을 제공

  1. DEFAULT : 사용하는 DB의 격리수준을 사용.

  1. READ_UNCOMMITTED : 커밋되지 않은 데이터를 다른 트랜잭션에서 접근 가능
    Dirty Read 가 가능하기 때문에 잘못된 데이터를 읽을 수 있습니다. = 커밋하지 않은 데이터도 들고와서 서비스 장애발생!

  1. READ-COMMITTED : 트랜잭션은 커밋한 데이터만 읽기 가능.
    각각의 트랜잭션이 데이터를 변경해도 커밋을 하지 않으면 이전의 데이터를 조회함.
    🤢Non-Repeatable Read 현상이 발생 할 수도 있음 ->트랜잭션에서 조회한 데이터가 트랜잭션이 끝나기 전에 다른 트랜잭션에 의해 변경되면 다시 읽었을 때 새로운 값이 읽히며 데이터 불일치하는 현상

  1. REPEATABLE_READ : 하나의 트랜잭션은 하나의 스냅샷만 사용

    A 트랜잭션이 시작하고 처음 조회한 데이터의 스냅샷을 저장하고 이후에 동일한 쿼리를 호출하면 스냅샷에서 데이터를 가져옵니다. 중간에 B 트랜잭션이 새로 커밋해도 A 트랜잭션이 조회하는 데이터는 변하지 않습니다.

원래는 다른 트랜잭션에서 commit이 일어나면 해당 undo log는 삭제하게 되는데, REPEATABLE READ에서는 이 구조를 유지하기 위하여, 요청한 트랜잭션 이전의 트랜잭션에 의해 생성된 언두 영역은 삭제 대상에 포함시키지 않고, 해당 데이터들만 볼 수 있도록 설계되어 있음


  1. SERIALIZABLE : 순차적으로 트랜잭션을 진행시키며 읽기 작업에도 잠금을 걸어 여러 트랜잭션이 동시에 같은 데이터에 접근하지 못합니다.
    가장 안전하지만 성능 저하가 발생하기 때문에 극도의 안정성을 필요로 하지 않으면 자주 사용되지 않음.

Read committed VS Repeatable read

두개의 차이점은 하나의 트랜잭션에서 데이터 값이 변경하느냐 안하느냐의 차이다

Read Committed의 경우
-> 하나의 트랜잭션에서 동일한 조회 과정 수행이 되는동안 다른 트랜잭션에서 커밋이 일어나면 동일한 조회의 결과값이 달라질 수 있다. ( 매 조회마다 새로운 SNAPSHOT을 생성)

Reaeatable read의 경우
-> 하나의 트랜잭션이 시작하기전에 첫 조회시 만들어진 SNAPSHOT을 기반으로 조회 결과를 반환한다. 그래서 다른 트랜잭션에서 작업이 일어나도 하나의 트랜잭션의 범위 안에서는 동일한 내용을 출력한다.

Mysql의 innoDB를 통해 Phantom Read를 방지하지만 Update 쿼리시 특이한 경우로 발생할 수 있다

Phantom Read
A : START TRANSACTION;
A : SELECT * FROM test; # 최초 Snapshot 생성

B : INSERT INTO test (id, name, age) VALUES (5, 'test', 26); # Lock 이 걸려있지 않기 때문에 Gap 에 데이터 삽입 가능

A : SELECT * FROM test; # 최초 Snapshot 을 그대로 사용
A : UPDATE test SET name = 'twenty-six' WHERE age = 26; # Locking Read 를 통해 새롭게 읽은 후 UPDATE
A : SELECT * FROM test; # Snapshot 이 초기화되어 Phantom Read 발생

출처


@Transactional(readOnly = true)

트랜잭션을 읽기 전용으로 변경합니다.

만약 읽기 전용 트랜잭션 내에서 INSERT, UPDATE, DELETE 작업을 해도 반영이 되지 않거나 DB 종류에 따라서 아예 예외가 발생하는 경우도 있습니다.

성능 향상을 위해 사용하거나 읽기 외의 다른 동작을 방지하기 위해 사용하기도 합니다.

왜 성능이 향상될까? -> JPA 에는 Dirty Checking 이라는 기능이 있음
개발자가 임의로 UPDATE 쿼리를 사용하지 않아도 트랜잭션 커밋 시에 1차 캐시에 저장되어 있는 Entity 와 스냅샷을 비교해서 변경된 부분이 있으면 UPDATE 쿼리를 날려주는 기능

readOnly = true 옵션을 주면 스프링 프레임워크가 하이버네이트의 FlushMode 를 MANUAL 로 설정해서 Dirty Checking 에 필요한 스냅샷 비교 등을 생략하기 때문에 성능이 향상

작성중

참고
참고2
참고3
참고4

profile
HelloWorld에서 RealWorld로

0개의 댓글