더보기

트렌젝션(Transaction) 이란?

  • 모든 작업들이 성공적으로 완료되어야 작업 묶음의 결과를 적용하고, 어떤 작업에서 오류가 발생했을 때는 이전에 있던 모든 작업들이 성공적이었더라도 없었던 일처럼 완전히 되돌리는 것
  • 데이터베이스를 다룰 때 트렌젝션을 적용하면 데이터 추가, 갱신, 삭데 등으로 이루어진 작업을 처리하던 중 오류가 발생했을 시 그동안 진행한 작업들을 모두 원상태로 되돌릴 수 있다.

 

스프링은 @Transactional 어노테이션을 이용한 선언적 트렌젝션 처리를 지원한다.

스프링에서 트렌젝션 처리를 하는 방법은 DB와 관련된 트렌젝션이 필요한 서비스 클래스 혹은 메서드에 @Transactional 어노테이션을 선언해주면 된다. 이 방식을 선언적 트렌젝션이라 부르며, 적용된 범위에서는 트렌젝션 기능이 포함된 프록시 객체가 생성되어 자동으로 커밋 혹은 롤백을 진행해준다.

 

@Transactional( rollbackFor = Exception.class )
@Override
public int dismissLecturer( EgovMap egovMap ) throws Exception {
    return lecturerDAO.dismissLecturer( egovMap );
}

@Transactional 이 붙은 메서드는 메서드가 포함하고 있는 작업 중에 하나라도 실패할 경우 전체 작업을 취소한다.

 

 

@Transactional 옵션

 

1. isolation

트랜잭션에서 일관성없는 데이터 허용 수준을 설정한다

2. propagation

트랜잭션 동작 도중 다른 트랜잭션을 호출할 때, 어떻게 할 것인지 지정하는 옵션이다

3. noRollbackFor

특정 예외 발생 시 rollback하지 않는다.

4. rollbackFor

특정 예외 발생 시 rollback한다.

5. timeout

지정한 시간 내에 메소드 수행이 완료되지 않으면 rollback 한다. (-1일 경우 timeout을 사용하지 않는다)

6. readOnly

트랜잭션을 읽기 전용으로 설정한다.



1. isolation (격리레벨)

적용방법

@Transactional(isolation=Isolation.DEFAULT)
public void addUser(UserDTO dto) throws Exception {
	// 로직 구현
}
  • DEFAULT : 기본 격리 수 : 기본이며, DB의 lsolation Level을 따른다.

 

  • READ_UNCOMMITED (level 0) - 커밋되지 않는 데이터에 대한 읽기를 허용

      : 어떤 사용자가 A라는 데이터를 B라는 데이터로 변경하는 동안 다른 사용자는 B라는 아직 완료되지 않은(Uncommitted

       혹은 Dirty)데이터 B를 읽을 수 있다.

       -Problem1 - Dirty Read 발생

 

  • READ_COMMITED (level 1) : 커밋된 데이터에 대해 읽기 허용

      : 어떠한 사용자가 A라는 데이터를 B라는 데이터로 변경하는 동안 다른 사용자는 해당 데이터에 접근할 수 없다.
      -Problem1 - Dirty Read 방지

 

  • REPEATEABLE_READ (level 2) : 동일 필드에 대해 다중 접근 시 모두 동일한 결과를 보장

      : 트랜잭션이 완료될 때까지 SELECT 문장이 사용하는 모든 데이터에 shared lock이 걸리므로 다른 사용자는 그 영역

      에 해당되는 데이터에 대한 수정이 불가능하다.
      : 선행 트랜잭션이 읽은 데이터는 트랜잭션이 종료될 때까지 후행 트랜잭션이 갱신하거나 삭제가 불가능 하기 때문

      에 같은 데이터를 두 번 쿼리했을 때 일관성 있는 결과를 리턴한다.
      -Problem2 - Non-Repeatable Read 방지

 

  • SERIALIZABLE (level 3) : 가장 높은 격리, 성능 저하의 우려가 있음

      : 데이터의 일관성 및 동시성을 위해 MVCC(Multi Version Concurrency Control)을 사용하지 않음.
      (MVCC는 다중 사용자 데이터베이스 성능을 위한 기술로 데이터 조회 시 LOCK을 사용하지 않고 데이터의 버전을 

      관리해 데이터의 일관성 및 동시성을 높이는 기술)
      : 트랜잭션이 완료될 때까지 SELECT 문장이 사용하는 모든 데이터에 shared lock이 걸리므로 다른 사용자는 그

      영역에 해당되는 데이터에 대한 수정 및 입력이 불가능하다.
      -Problem3 - Phantom Read 방지

 

격리수준에 따른 문제

IsolationLevel Dirty Read  Non-Repeatable Read Phantom Read
Read Uncommitted O O O
Read Committed - O O
Repeatable Read - - O
Serializable - - -

 

Dirty Read

 

트랜잭션 1이 수정중인 데이터를 트랜잭션 2가 읽을 수 있다. 만약 드랜잭션 1의 작업이 정상 커밋되지 않아 롤백되면, 트랜잭션 2가 읽었던 데이터는 잘못된 데이터가 되는 것이다. (데이터 정합성에 어긋남)

 

 

Non-repeatable read

 

트랜잭션 1이 회원 A를 조회중에 트랜잭션 2가 회원 A의 정보를 수정하고 커밋한다면, 트랜잭션 1이 다시 회원A를 조회했을 때는 수정된 데이터가 조회된다. (이전 정보를 다시 조회할 수 없음). 이처럼 반복해서 같은 데이터를 읽을 수 없는 경우이다.

 

 

Phantom read

 

트랜잭션 1이 10살 이하의 회원을 조회했는데 트랜잭션 2가 5살 회원을 추가하고 커밋하면 트랜잭션 1이 다시 10살 이하 회원을 조회했을 때 회원 한명이 추가된 상태로 조회된다. 이처럼 반복 조회시 결과집합이 달라지는 경우이다.

 

트랜잭션 격리 수준의 필요성

 

당연히 레벨이 높아질 수록 데이터 무결성을 유지할 수 있다.
하지만, 무조건적인 상위 레벨을 사용할 시 Locking으로 동시에 수행되는 많은 트랜잭션들이 순차적으로 처리하게 되면서 DB의 성능은 떨어지게 되고 비용이 높아진다.
그렇다고 Locking의 범위를 줄이게 되면 잘못된 값이 처리될 여지도 발생한다.
그러므로 최대한 효율적인 방안을 찾아 상황에 맞게 사용하는 것이 중요하다.

 

 

2. propagation (전파속성)

 

 

적용방법

@Transactional(propagation=Propagation.REQUIRED)
public void addUser(UserDTO dto) throws Exception {
	// 로직 구현
}

 

  • REQUIRED (Defualt)
이미 진행중인 트랜잭션이 있다면 해당 트랜잭션 속성을 따르고, 진행중이 아니라면 새로운 트랜잭션을 생성한다.
  • REQUIRES_NEW
항생 새로운 트랜잭션을 생성한다. 이미 진행중인 트랜잭션이 있다면 잠깐 보류하고 해당 트랜잭션 작업을 먼저 진행한다
  • SUPPORT
이미 진행 중인 트랜잭션이 있다면 해당 트랜잭션 속성을 따르고, 없다면 트랜잭션을 설정하지 않는다.
  • NOT_SUPPORT
이미 진행중인 트랜잭션이 있다면 보류하고, 트랜잭션 없이 작업을 수행한다.
  • MANDATORY
이미 진행중인 트랜잭션이 있어야만, 작업을 수행한다. 없다면 Exception을 발생시킨다.
  • NEVER
트랜잭션이 진행중이지 않을 때 작업을 수행한다. 트랜잭션이 있다면 Exception을 발생시킨다.
  • NESTED
진행중인 트랜잭션이 있다면 중첩된 트랜잭션이 실행되며, 존재하지 않으면 REQUIRED와 동일하게 실행된다.

 

3. noRollbackFor (예외무시)

 

특정예외 발생 시 Rollback 처리 하지 않음.

 

적용방법

@Transactional(noRollbackFor=Exception.class)
public void addUser(UserDTO dto) throws Exception {
	// 로직 구현
}

 

4. rollbackFor (예외추가)

 

특정 예외 발생 시 강제로 Rollback

 

적용방법

@Transactional(rollbackFor=Exception.class)
public void addUser(UserDTO dto) throws Exception {
	// 로직 구현
}

 

5. timeout (시간지정)

 

지정한 시간 내에 해당 메소드 수행이 완료되지 않을 경우 rollback 수행

 

-1일 경우 no timeout
Default = -1

 

적용방법

@Transactional(timeout=10)
public void addUser(UserDTO dto) throws Exception {
	// 로직 구현
}

 

6. readOnly (읽기전용)

 

true 시 insert, update, delete 실행 시 예외 발생


Default = flase

 

적용방법

@Transactional(readonly = true)
public void addUser(UserDTO dto) throws Exception {
	// 로직 구현
}

 


 

@Transactional은 기본적으로 Unchecked Exception, Error 만을 Rollback 하고 있다.

그렇기 때문에 모든 예외에 대해서 Rollback을 진행하고 싶은 경우

(rollbackFor = Exception.class)를 붙여야 한다.

+ Recent posts