상황

스프링 환경에서 일반적으로 DAO 나 BO 레벨에서 다음과 같이 코딩을 하게 된다.

1
2
3
4
@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public int method(int i) throws Exception {
return sqlMapClient.delete("~~~~");
}

무분별하게 Ctrl+C,V 신공으로 트랜잭션 어노테이션을 가져다가 사용할수도 있겠으나, 각 값들이 어떤 역활을 아는지에 대해 알고 넘어갈 필요성이 있다.

@Transactional

우선 해당 어노테이션을 적용하면 적용된 클래스 또는 메소드에 트랜잭션이 적용된다. 따라서 로직 흐름에 맞추어 전체적으로 트랜잭션을 적용할것인지, 아니면 특정 메소드에 적용할것인지 전략을 잘 세워야 한다.

isolation

격리수준이라는 옵션이다. 트랜잭션에서 일관성이 없는 데이터를 허용하도록 하는 수준을 말하는데 옵션은 다음과 같다.

  1. READ_UNCOMMITTED (level 0)

    • 트랜잭션에 처리중인 혹은 아직 커밋되지 않은 데이터를 다른 트랜잭션이 읽는 것을 허용
    • 어떤 사용자가 A라는 데이터를 B라는 데이터로 변경하는 동안 다른 사용자는 B라는 아직 완료되지 않은(Uncommitted 혹은 Dirty) 데이터 B를 읽을 수 있다.

      Dirty read
      위와 같이 다른 트랜잭션에서 처리하는 작업이 완료되지 않았는데도 다른 트랜잭션에서 볼 수 있는 현상을 dirty read 라고 하며, READ UNCOMMITTED 격리수준에서만 일어나는 현상

  2. READ_COMMITTED (level 1)

    • dirty read 방지 : 트랜잭션이 커밋되어 확정된 데이터만을 읽는 것을 허용
    • 어떠한 사용자가 A라는 데이터를 B라는 데이터로 변경하는 동안 다른 사용자는 해당 데이터에 접근할 수 없다.
  3. REPEATABLE_READ (level 2)

    • 트랜잭션이 완료될 때까지 SELECT 문장이 사용하는 모든 데이터에 shared lock이 걸리므로 다른 사용자는 그 영역에 해당되는 데이터에 대한 수정이 불가능하다.
    • 선행 트랜잭션이 읽은 데이터는 트랜잭션이 종료될 때까지 후행 트랜잭션이 갱신하거나 삭제하는 것을 불허함으로써 같은 데이터를 두 번 쿼리했을 때 일관성 있는 결과를 리턴함
  4. SERIALIZABLE (level 3)

    • 완벽한 읽기 일관성 모드를 제공
    • 데이터의 일관성 및 동시성을 위해 MVCC(Multi Version Concurrency Control)을 사용하지 않음(MVCC는 다중 사용자 데이터베이스 성능을 위한 기술로 데이터 조회 시 LOCK을 사용하지 않고 데이터의 버전을 관리해 데이터의 일관성 및 동시성을 높이는 기술)
    • 트랜잭션이 완료될 때까지 SELECT 문장이 사용하는 모든 데이터에 shared lock이 걸리므로 다른 사용자는 그 영역에 해당되는 데이터에 대한 수정 및 입력이 불가능하다.

propagation ( 전파옵션)

  • REQUIRED : 부모 트랜잭션 내에서 실행하며 부모 트랜잭션이 없을 경우 새로운 트랜잭션을 생성
  • REQUIRES_NEW : 부모 트랜잭션을 무시하고 무조건 새로운 트랜잭션이 생성
  • SUPPORT : 부모 트랜잭션 내에서 실행하며 부모 트랜잭션이 없을 경우 nontransactionally로 실행
  • MANDATORY : 부모 트랜잭션 내에서 실행되며 부모 트랜잭션이 없을 경우 예외가 발생
  • NOT_SUPPORT : nontransactionally로 실행하며 부모 트랜잭션 내에서 실행될 경우 일시 정지
  • NEVER : nontransactionally로 실행되며 부모 트랜잭션이 존재한다면 예외가 발생
  • NESTED : 해당 메서드가 부모 트랜잭션에서 진행될 경우 별개로 커밋되거나 롤백될 수 있음. 둘러싼 트랜잭션이 없을 경우 REQUIRED와 동일하게 작동

no-rollback-for - 예외처리 (기본값 : 없음)

특정 예외가 발생하더라도 롤백되지 않도록 설정

스프링 배치에서의 트랜잭션 (내가 당했던(?) 문제)

스프링 배치에서는 Tasklet 에서 기본적으로 step 단위 트랜잭션을 지원하고 있다고 한다.
기본적으로 job이 하나의 tasklet 의 step 으로 실행하다보니 명시적이진 않지만 내부적으로 전체 트랜잭션으로 걸려있게 된다. 나같은 job 내 DAO delete 메소드에서 @Transactional 설정을 하고 그 DAO 메소드를 반복문에 의해 delete 하는 로직을 수행하는 부분이 있었는데 부모의 트랜잭션(tasklet에서 설정된 트랜잭션)으로 인해 dao 를 몇번 호출하던 job단위로 트렌젝션이 걸리게 되었다. (결국 트랜잭션은 반복문이 다 끝나야 적용이 된다는점.)
그러다보니 가끔 DB Query Lock이 걸렸는데 DB레벨에서 undolog를 남기는게 너무 무거워져 lock이 발생

따라서 전파옵션을 수정해서 해당 문제를 해결하였다.

1
2
3
4
5
6
7
8
9
10
11
# 기존
begin
delete < for 반복문
commit

# 전파옵션 수정 (기존 REQUIRES 에서 REQUIRES_NEW 으로 수정)
for
begin
delete
commit
for end