Spring) @Transactional에 대해

10140 단어 SpringSpring

서론

회사 코드를 보면서 Controller와 Service에 어떤 코드를 넣어야 하는가에 대한 의문이 생겼다.
Service에는 비즈니스 로직을 코딩하라고 하지만 비즈니스 로직도 어떤 크기에 따라 나눠야하는지에 대해 고민을 하다가 @Transactional에 대해 알게 되었다. Transaction 단위로 개발을 위해 @Transactional에 대해 공부하자


트랜잭션의 성질

1. 원자성

트랜잭션 내에서 실행한 작업들은 하나로 간주한다. 즉, 모두 성공 또는 모두 실패

2. 일관성

트랜잭션은 일관성 있는 데이터베이스 상태를 유지한다.

3. 격리성

동시에 실행되는 트랜잭션들은 서로 영향을 미치지 않도록 격리해야한다.

4. 지속성

트랜잭션을 성공적으로 마치면 결과가 항상 저장되어야 한다.

Spring에서는 트랜잭션 처리를 지원하는데 @Transactional을 선언하여 사용하는 방법이 일반적이고, 이를 선언적 트랜잭션이라고 부른다.

@Transactional 속성

isolation (격리수준)

트랜잭션에서 일관성이 없는 데이터를 허용하도록 하는 수준을 말한다.
  • DEFAULT : 기본 격리 수준

  • READ_UNCOMMITED (level 0)
    - 커밋되지 않는 데이터에 대한 읽기를 허용
    - 즉 어떤 사용자가 A라는 데이터를 B라는 데이터로 변경하는 동안 다른 사용자는 B라는 아직 완료되지 않은 데이터를 읽을 수 있다.
    Problem : Dirty Read 발생

  • READ_COMMITED (level 1)
    - 트랜잭션이 커밋 된 확정 데이터만 읽기 허용
    - 어떠한 사용자가 A라는 데이터를 B라는 데이터로 변경하는 동안 다른 사용자는 해당 데이터에 접근할 수 없다.
    Problem : Dirty Read 방지

  • REPEATABLE_READ (level 2)
    - 트랜잭션이 완료될 때까지 SELECT 문장이 사용하는 모든 데이터에 shared lock이 걸리므로 다른 사용자는 그 영역에 해당되는 데이터에 대한 수정이 불가능하다.
    - 선행 트랜잭션이 읽은 데이터는 트랜잭션이 종료될 때까지 후행 트랜잭션이 갱신하거나 삭제가 불가능하기 때문에 같은 데이터를 두번 쿼리할 때 일관성 있는 결과를 리턴한다.
    Problem : Non-Repeatable Read 방지

    shared lock (공유 잠금) 이란?
    읽기 잠금이라고도 불린다. 리소스를 다른 사용자가 동시에 읽을 수 있게 하지만 변경은 불가능하게 하는 것이다.
    Exclusive lock 과 Shared lock의 차이

  • SERIALIZABLE (level 3)
    - 데이터의 일관성 및 동시성을 위해 MVCC(Multi Version Concurrency Control)을 사용하지 않음
    - 트랜잭션이 완료될 때까지 SELECT 문장이 사용하는 모든 데이터에 shared lock이 걸리므로 다른 사용자는 그 영역에 해당되는 데이터에 대한 수정 및 입력이 불가능하다.
    Problem : Phantom Read 방지

    MVCC란?
    다중 사용자 데이터베이스 성능을 위한 기술로 데이터 조회 시 LOCK을 사용하지 않고 데이터의 버전을 관리해 데이터의 일관성 및 동시성을 높이는 기술

@Transaction(isolation=Isolation.DEFAULT)
public void test(String a) {...}

propagation (전파옵션)

트랜잭션 동작 도중 다른 트랜잭션을 호출하는 상황에 선택할 수 있는 옵션이다.
  • Propagation.REQUIRED
    - 기본적으로 해당 메소드를 호출한 곳에서 트랜잭션 설정이 없다면, 트랜잭션을 새로 시작한다. (새로운 연결을 생성하고 실행한다.)
    - 이미 트랜잭션이 설정이 되어있다면, 기존 트랜잭션 객체를 연결하여 사용한다.

해당 메소드는 트랜잭션이 필요하다. 트랜잭션을 새로 열거나 기존에 트랜잭션을 사용한다.

@Transaction(propagation = Propagation.REQUIRED)
public void doSomething() {...}
  • Propagation.SUPPORT

    부모 트랜잭션이 존재하면 트랜잭션을 실행하고 없으면 필요없다.

@Transactional(propagation = Propagation.SUPPORT)
public void doSomething() {...}
  • Propagation.MANDATORY

    부모 트랜잭션이 존재하면 부모 트랜잭션으로 동작하고 없으면 예외 발생

@Transactional(propagation = Propagation.MANDATORY)
public void doSomething() {...}
  • Propagation.REQUIRES_NEW
    - 매번 새로운 트랜잭션이 실행된다. 만약, 호출한 곳에서 이미 트랜잭션이 설정되어 있다면 기존의 트랜잭션은 메소드가 종료할 때까지 잠시 대기상태로 두고 자신의 트랜잭션을 실행한다.
    - 즉, 두개의 트랜잭션이 완전 독립적으로 동작한다.

메소드 자기 자신만의 트랜잭션을 필요로 한다.

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void doSomething() {...}
  • Propagation.NOT_SUPPORT
    - 트랜잭션 없이 실행되며 이미 트랜잭션이 존재하면 일시 정지한다.

트랜잭션 필요없고 이미 실행중인 트랜잭션이 있으면 중지시킨다.

@Transactional(propagation = Propagation.NOT_SUPPORT)
public void doSomething() {...}
  • Propagation.NEVER

이미 실행중인 트랜잭션이 존재하면 에러 발생

@Transactional(propagation = Propagation.NEVER)
public void doSomething() {...}
  • Propagation.NESTED
    - 해당 메소드가 부모 트랜잭션에서 진행될 경우 별개로 커밋되거나 콜백될 수 있다.
    부모 트랜잭션이 없을 경우에는 REQUIRED와 동일하게 작동한다.
    차이점은 SAVEPOINT를 지정한 시점까지 부분 롤백이 가능하다. 하지만 DB에서 SAVEPOINT
    를 사용이 가능할 때 사용할 수 있다. 또한 이미 진행중인 트랜잭션이 있다면 중첩 트랜잭션을 시작한다.

SAVEPOINT를 활용해서 부분 롤백이 가능하다.

@Transactional(propagation = Propagation.NESTED)
public void doSomething() {...}

참조 자료1
참조 자료2
참조 자료3
참조 자료3

좋은 웹페이지 즐겨찾기