[Spring/DB] @Transactional의 전파 속성과 고립 레벨

신은지·2022년 10월 31일
2

시작 전에 읽어두기

  • 트랜잭션
    • DB 상태를 변화시키기 위해 수행하는 작업 단위
    • 즉, 유저(서비스)가 정의한 쿼리 묶음
      • Ex. 로그인 : 중복 ID 확인 (select)와 신규 계정 생성 (insert)
  • 트랜잭션의 ACID
    • 원자성 (Atomicity) : 트랜잭션은 완전히 성공하거나 완전히 실패하거나 둘 중 하나의 상태를 가져야 한다.
    • 일관성 (Consistency) : 트랜잭션의 작업 처리 결과는 항상 일관적이어야 한다.
      • Ex. 트랜잭션 진행 중 DB가 변경되어도, 처음 참조한 DB로 트랜잭션을 진행해야 한다.
    • 격리성 (Isolation) : 둘 이상의 트랜잭션이 실행될 때, 그 어떤 트랜잭션도 다른 트랜잭션의 연산을 침범할 수 없다.
    • 지속성 (Durability) : 트랜잭션이 성공적으로 완료되었을 때, 해당 트랜잭션의 결과는 DB에 영구적으로 반영되어야 한다.

전파 속성 (Propagation)

spring boot @Transactional에서는 Propagation.REQUIRED가 default로 세팅되어 있다

전파 속성이란?

  • SQL단에서 제공하는게 아니라 spring 단에서 제공하는 속성

    • Spring은 @Transactional (선언적 트랜잭션)을 이용해 여러 트랜잭션을 묶어 하나의 큰 트랜잭션 경계를 만들 수 있다.
    • 따라서 기존 트랜잭션 작업 중, 추가 트랜잭션 작업을 진행해야 할 때 해당 작업을 어떻게 진행할지 흐름을 결정해야 한다.
      • Ex. 추가 트랜잭션을 기존 트랜잭션의 일부로 볼 것인지, 아니면 별도로 볼 것인지, 아님 에러를 발생시키던지..!
    • 즉, 이 추가 트랜잭션의 흐름을 결정하는 옵션이 전파 속성!
  • 요런 느낌

      @Transactional
      public void parent() {
        deviceRepository.save(new Device());
        child();
      }
    
      @Transactional
      public void child() {
        deviceRepository.save(new Device());
      }

Spring Boot @Transactional 옵션 정리

  • 부모 트랜잭션 : 호출한 메소드의 트랜잭션
  • 자식 트랜잭션 : 호출당한 메소드의 트랜잭션
  • Propagation.REQUIRED
    • 부모 트랜잭션이 존재한다면, 자식 트랜잭션은 부모에게 포함된다.
      • 즉, 자식 트랜잭션이 실패하면 부모도 롤백된다
    • 부모 트랜잭션이 존재하지 않다면, 새로운 트랜잭션을 생성한다 (즉 자식이 부모가 된다)
  • Propagation.REQUIRES_NEW
    • 부모 트랜잭션의 존재 유무와 관계없이, 새로운 트랜잭션을 생성한다.
  • Propagation.NESTED
    • 부모 트랜잭션이 존재할 경우, 부모 트랜잭션 안에 새로운 트랜잭션을 생성한다.
    • 단, 자식 트랜잭션은 부모 트랜잭션의 영향(커밋, 롤백)을 받지만 부모는 자식의 영향을 받지 않는다.
      • 부모를 롤백하면 자식도 롤백되지만, 자식을 롤백해도 부모는 영향 X
  • Propagation.MANDATORY
    • 부모가 존재하면 부모에 포함
    • 부모가 존재하지 않는다면 예외 발생
  • Propagation.SUPPORTS
    • 부모가 존재하면 부모에 포함
    • 부모가 존재하지 않는다면 트랜잭션 없이 동작
  • Propagation.NOT_SUPPORTED
    • 부모가 존재하면 부모 트랜잭션을 보류하고, 트랜잭션 없이 동작
    • 부모가 존재하지 않는다면 트랜잭션 없이 동작
  • Propagation.NEVER
    • 트랜잭션을 사용하지 않도록 강제한다.
    • 부모가 존재할 경우, 예외 발생

고립 레벨 (Isolation)

spring boot @Transactional에서는 Isolation.DEFAULT, 즉 JDBC isolation level가 default로 세팅되어 있다.
:: 즉 MySQL InnoDB를 사용한다면 REPEATABLE READ 사용

고립 레벨이란?

  • Shared Lock (공유 잠금) : 읽기 잠금
  • Exclusive Lock (배타적 잠금) : Insert, Update, Delete (=쓰기) 잠금
  • SQL의 Isolation level과 동일하게 동작
  • SQL의 고립 레벨 (= 고립 수준 = 격리 레벨 = 격리수준)이란?
    • 트랜잭션끼리 일관성 없는 데이터를 허용하는 수준
  • 종류
    • [Level 0] Read Uncommitted : select 수행하는 동안 해당 데이터에 Shared lock이 걸리지 않는다.
    • [Level 1] Read Committed : select 수행하는 동안 해당 데이터에 Shared lock이 걸린다.
    • [Level 2] Repeatable Read : 트랜잭션이 완료될 때까지 해당 데이터 수정이 불가능하다.
    • [Level 3] Serializable : 트랜잭션이 완료될 때까지 해당 데이터 수정, 입력이 불가능하다.

고립 레벨별 나타나는 이슈

  • Dirty Read : Uncommitted 또는 더티 버퍼에 있는 데이터를 읽어, 롤백된 데이터를 노출하게 됨
  • Non Repeatable Read : 동일한 쿼리를 2번(A와 B라고 가정) 실행 할 때, A와 B 사이에 수정 또는 삭제가 일어나 A와 B의 결과가 달라짐 (=행 값의 변화)
  • Phantom Read : 동일한 쿼리를 2번(A와 B라고 가정) 실행할 때, A와 B 사이에 삽입이 일어나 A와 B의 결과가 달라짐 (=행 수의 변화)
Isolation LevelDirty ReadNon Repeatable ReadPhantom Read
READ UNCOMMITTEDPermittedPermittedPermitted
READ COMMITTEDPermittedPermitted
REPEATABLE READPermitted
SERIALIZABLE

DB별 default isolation

  • MySQL InnoDB : REPEATABLE READ
  • Oracle : READ COMMITTED

Spring Boot @Transactional 옵션 정리

SQL의 고립레벨과 동일!!!

Ex. 홍길동씨와 강동원씨가 공유 통장에 대해 아래 작업을 요청할 때
1. 통장 잔고 확인 (=select)
2. 통장 잔고 수정 (=update)

  • Isolation.READ_UNCOMMITTED (level 0)
    • 홍길동씨가 잔고 수정 작업을 끝마치기 전, 강동원씨는 통장 잔고를 확인할 수 있다.
  • Isolation.READ_COMMITTED (level 1)
    • 홍길동씨가 잔고 수정을 끝마치기 전까지, 강동원씨는 통장 잔고를 확인할 수 없다.
  • Isolation.REPEATABLE_READ (level 2)
    • 홍길동씨가 잔고 확인, 잔고 수정 작업을 끝마칠 때까지 강동원씨는 아무 작업도 시작할 수 없다.
    • 단, 입금의 경우 (=insert) 처리할 수 있다. 하지만 홍길동씨와 강동원씨의 잔고 확인 결과가 달라질 수 있다.
  • Isolation.SERIALIZABLE (level 3)
    • 홍길동씨가 잔고 확인, 잔고 수정 작업을 끝마칠 때까지 강동원씨는 아무 작업도 시작할 수 없다 (입금도 불가능).
profile
호그와트 장학생

0개의 댓글