시작하며
트랜잭션과 격리 수준, @Transaction
어노테이션 세부 설정에 대해 작성해보려고한다.
트랜잭션이란?
데이터베이스의 상태를 변화시키기 위해 수행하는 작업 단위
여기서 작업 단위란 무엇일까?
예를 들어, A라는 사람이 B라는 사람에게 돈을 송금한다고 가정했을 때
1. A사람 계좌의 잔액을 차감한다.
2. 차감한 잔액 만큼 B사람의 계좌 잔액을 증가시킨다.
1번 작업(A사람 계좌에서 잔액을 차감한다)를 진행 후 2번 작업(B사람의 잔액을 증가시킨다)을 하려던 중에 시스템이 멈춰버린다면
A사람의 잔액만 줄어든 채 B라는 사람은 돈을 못받는 현상(?)이 발생한다.
인간에게는 송금
이라는 하나의 작업이지만, 컴퓨터에게는 두 번의 작업이다.
두 번의 작업은 송금
이라는 관점에서 볼 때 하나의 작업으로 묶여야만 한다.
트랜잭션 성질
A계좌 잔액 줄이기
작업과 B계좌 잔액 늘리기
작업은 함께 성공하거나 함께 실패해야 한다.트랜잭션의 작업은 일관적이여야 한다.
모든 트랜잭션이 종료된 후에는 DB의 제약조건을 모두 지키고 있는 상태가 되어야 한다.
예시) 잔액은 0원 이상이다. 이를 위반하는 트랜잭션은 모두 중단된다.
여러 트랜잭션이 경쟁하면 생기는 문제
트랜잭션A
가 User
테이블의 2번 째 row를 수정하던 중
트랜잭션B
가 User
테이블의 2번 째 row를 조회하려고 하는 상황에서
트랜잭션A
가 Rollback 된다면 트랜잭션B
는 Rollback 되기 전 값을 읽어가는 문제가 발생한다.
트랜잭션A
가 User
테이블의 2번 째 row를 2번 조회 하는 상황에서 첫 번째 조회와 두 번째 조회 사이에
트랜잭션B
가 User
테이블의 3번 째 row를 수정하고 커밋한다면
트랜잭션A
의 입장에서는 첫 번째 조회해온 값과 두 번째 조회해온 값이 불일치 하게 된다.
그렇게 되면 트랜잭션의 성질 중 Consistency(일관성) 트랜잭션의 작업은 일관적이여야 한다.
을 만족시키지 못한다.
Phantom Read
는 Non-Repeatable Read
와 유사하다.
Non-Repeatable Read
은 특정 값을 두 트랜잭션이 경쟁했을 때 생기는 문제고
Phantom Read
는 특정 범위에서 두 트랜잭션이 경쟁했을 때 생기는 문제이다.
Spring
@Transacion
어노테이션 세부설정
트랜잭션에서 일관성이 없는 데이터를 허용하는 수준
@Transactional(isolation=Isolation.DEFAULT)
DEFAULT
READ_UNCOMMITTED
(Dirty Read 발생)READ_COMMITTED
(Dirty Read 방지)REPEATABLE_READ
(Non_Repeatable Read 방지)SERIALIZABLE
(Phantom Read 방지)위에서 아래 순서로 성능은 떨어지고 격리성(고립성)은 증가한다.
일반적으로는 MYSQL InnoDB의 기본 값인 REPEATABLE_READ
를 많이 활용한다.
레벨 | Dirty Read | Non-Repeatable Read | Phantom Read |
---|---|---|---|
Read Uncommitted | 가능 | 가능 | 가능 |
Read Committed | 불가능 | 가능 | 가능 |
Repeatable Read | 불가능 | 불가능 | 가능 |
Serializable Read | 불가능 | 불가능 | 불가능 |
트랜잭션 동작 도중 다른 트랜잭션을 호출하는 상황
@Transactional(propagation = Propagation.REQUIRED)
트랜잭션을 시작하거나
기존 트랜잭션에 참여하는 방법에 대해 결정하는 속성 값
REQUIRED
(DEFAULT)SUPPORTS
REQUIRES_NEW
NESTED
REQUIRED
는 부모 트랜잭션에서 자식 트랜잭션을 시작할 때 부모 트랜잭션 안에서 자식 트랜잭션까지 함께 실행한다.
SUPPORTS
는 이미 시작된 트랜잭션이 있다면 참여하고,
없다면 트랜잭션 없이 진행한다.
REQUIRES_NEW
부모 트랜잭션 안에서 자식 트랜잭션이 시작 될 때 자식 트랜잭션이 새로 시작된다.
NESTED
이미 진행되는 트랜잭션이 있는 경우에, 중첩 트랜잭션이 시작된다.
중첩 트랜잭션이란 이미 동작하고 있는 트랜잭션 안에 트랜잭션 하나를 더 만든다.
NESTED
에서 먼저 시작된 부모 트랜잭션이 커밋되거나 롤백된다면 자식 트랜잭션은 영향을 받지만,
자식 트랜잭션에서 커밋되거나 롤백되거나 부모 트랜잭션에게는 영향을 주지 않는다.
NESTED
사용 예시
ex)게시글을 저장할 때 로그를 DB에 저장하는 상황
1.로그 저장이 실패한다고 해서 게시글 저장까지 롤백되면 안됨
2.게시글 저장이 실패하면 로그 저장까지 롤백되어야 함
트랜잭션을 읽기 전용 속성으로 지정
@Transactional(readOnly = true)
기본적으로 readOnly 속성을 지정하지 않으면 false이다.
이 속성을 지정하여 읽기 전용 Transaction으로 선언하는 것이다.
성능을 최적화 하거나 특정 트랜잭션 작업 안에서 읽기 외에 쓰기, 삭제, 수정과 같은 작업이 일어나는 것을 의도적으로 방지한다.
readOnly 속성을 지정하고 그 트랜잭션 안에서 쓰기, 수정, 삭제 가 일어나게 된다면 예외가 발생한다.
예외가 발생했을 때 트랜잭션 롤백시킬 경우를 설정
@Transactinal(rollbackFor=Exception.class)
@Transactinal(noRollbackFor=Exception.class)
Default:RuntimeException, Error
특정 Exception이 발생하였을때는 rollback을 안하고 싶다 라고 한다면
noRollBackfor을 지정해주면 해당 Exception에 대해서는 rollback을 진행하지 않는다.
일정 시간 내에 트랜잭션을 끝내지 못하면 롤백
@Transactional(timeout=10)