트랜잭션(Transaction)과 격리 수준

도모·2022년 11월 11일
2
post-thumbnail

시작하며

트랜잭션과 격리 수준, @Transaction 어노테이션 세부 설정에 대해 작성해보려고한다.

트랜잭션이란?

데이터베이스의 상태를 변화시키기 위해 수행하는 작업 단위

여기서 작업 단위란 무엇일까?

예를 들어, A라는 사람이 B라는 사람에게 돈을 송금한다고 가정했을 때
1. A사람 계좌의 잔액을 차감한다.
2. 차감한 잔액 만큼 B사람의 계좌 잔액을 증가시킨다.

1번 작업(A사람 계좌에서 잔액을 차감한다)를 진행 후 2번 작업(B사람의 잔액을 증가시킨다)을 하려던 중에 시스템이 멈춰버린다면
A사람의 잔액만 줄어든 채 B라는 사람은 돈을 못받는 현상(?)이 발생한다.

인간에게는 송금이라는 하나의 작업이지만, 컴퓨터에게는 두 번의 작업이다.

두 번의 작업은 송금 이라는 관점에서 볼 때 하나의 작업으로 묶여야만 한다.

트랜잭션 성질

Atomic(원자성)

  • All or Nothing, 모든 작업이 실행되거나 혹은 모두 실행되지 않아야 한다.
    예시) A계좌에서 B계좌로 잔액을 송금할 때 A계좌 잔액 줄이기 작업과 B계좌 잔액 늘리기 작업은 함께 성공하거나 함께 실패해야 한다.

Consistency(일관성)

  • 트랜잭션의 작업은 일관적이여야 한다.

  • 모든 트랜잭션이 종료된 후에는 DB의 제약조건을 모두 지키고 있는 상태가 되어야 한다.

  • 예시) 잔액은 0원 이상이다. 이를 위반하는 트랜잭션은 모두 중단된다.

Isolation(격리성)

  • 트랜잭션은 다른 트랜잭션과 독립적으로 동작해야한다.
  • A트랜잭션이 하는 일을 B트랜잭션은 모르게 해야한다.
  • 하지만 안정성의 격리성은 트레이드 오프 관계에 있다.

Durability(지속성)

  • commit을 하게 되면 지속(저장)이 꼭 된다.
    DB 저장이 실패하더라도 모든 로그를 모두 남겨서 DB에 순차적으로 모두 반영이 되도록 한다.

여러 트랜잭션이 경쟁하면 생기는 문제

1. Dirty Read

트랜잭션AUser 테이블의 2번 째 row를 수정하던 중
트랜잭션BUser 테이블의 2번 째 row를 조회하려고 하는 상황에서

트랜잭션A가 Rollback 된다면 트랜잭션B는 Rollback 되기 전 값을 읽어가는 문제가 발생한다.

2.Non-Repeatable Read

트랜잭션AUser 테이블의 2번 째 row를 2번 조회 하는 상황에서 첫 번째 조회와 두 번째 조회 사이에
트랜잭션BUser 테이블의 3번 째 row를 수정하고 커밋한다면

트랜잭션A의 입장에서는 첫 번째 조회해온 값과 두 번째 조회해온 값이 불일치 하게 된다.

그렇게 되면 트랜잭션의 성질 중 Consistency(일관성) 트랜잭션의 작업은 일관적이여야 한다.을 만족시키지 못한다.

3.Phantom Read

Phantom ReadNon-Repeatable Read와 유사하다.

Non-Repeatable Read은 특정 을 두 트랜잭션이 경쟁했을 때 생기는 문제고

Phantom Read는 특정 범위에서 두 트랜잭션이 경쟁했을 때 생기는 문제이다.

Spring @Transacion 어노테이션 세부설정

1.Isolation (격리 수준)

트랜잭션에서 일관성이 없는 데이터를 허용하는 수준
@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 ReadNon-Repeatable ReadPhantom Read
Read Uncommitted가능가능가능
Read Committed불가능가능가능
Repeatable Read불가능불가능가능
Serializable Read불가능불가능불가능

2.Propagation (전파 수준)

트랜잭션 동작 도중 다른 트랜잭션을 호출하는 상황
@Transactional(propagation = Propagation.REQUIRED)

트랜잭션을 시작하거나
기존 트랜잭션에 참여하는 방법에 대해 결정하는 속성 값

  • REQUIRED (DEFAULT)
  • SUPPORTS
  • REQUIRES_NEW
  • NESTED
  • ... 더 많은 옵션이 있지만 4가지만 소개한다.

REQUIRED는 부모 트랜잭션에서 자식 트랜잭션을 시작할 때 부모 트랜잭션 안에서 자식 트랜잭션까지 함께 실행한다.

SUPPORTS는 이미 시작된 트랜잭션이 있다면 참여하고,
없다면 트랜잭션 없이 진행한다.

REQUIRES_NEW 부모 트랜잭션 안에서 자식 트랜잭션이 시작 될 때 자식 트랜잭션이 새로 시작된다.

NESTED 이미 진행되는 트랜잭션이 있는 경우에, 중첩 트랜잭션이 시작된다.
중첩 트랜잭션이란 이미 동작하고 있는 트랜잭션 안에 트랜잭션 하나를 더 만든다.



NESTED 에서 먼저 시작된 부모 트랜잭션이 커밋되거나 롤백된다면 자식 트랜잭션은 영향을 받지만,
자식 트랜잭션에서 커밋되거나 롤백되거나 부모 트랜잭션에게는 영향을 주지 않는다.

NESTED 사용 예시

ex)게시글을 저장할 때 로그를 DB에 저장하는 상황

1.로그 저장이 실패한다고 해서 게시글 저장까지 롤백되면 안됨
2.게시글 저장이 실패하면 로그 저장까지 롤백되어야 함

3.readOnly 속성

트랜잭션을 읽기 전용 속성으로 지정
@Transactional(readOnly = true)

기본적으로 readOnly 속성을 지정하지 않으면 false이다.
이 속성을 지정하여 읽기 전용 Transaction으로 선언하는 것이다.

성능을 최적화 하거나 특정 트랜잭션 작업 안에서 읽기 외에 쓰기, 삭제, 수정과 같은 작업이 일어나는 것을 의도적으로 방지한다.

readOnly 속성을 지정하고 그 트랜잭션 안에서 쓰기, 수정, 삭제 가 일어나게 된다면 예외가 발생한다.

4.트랜잭션 롤백 예외

예외가 발생했을 때 트랜잭션 롤백시킬 경우를 설정
@Transactinal(rollbackFor=Exception.class)
@Transactinal(noRollbackFor=Exception.class)

Default:RuntimeException, Error

특정 Exception이 발생하였을때는 rollback을 안하고 싶다 라고 한다면
noRollBackfor을 지정해주면 해당 Exception에 대해서는 rollback을 진행하지 않는다.

5.Timeout 속성

일정 시간 내에 트랜잭션을 끝내지 못하면 롤백
@Transactional(timeout=10)

profile
안녕하세요. 백엔드 엔지니어 도모입니다.

0개의 댓글