본문 바로가기

IT

트랜잭션(Transaction)

728x90

트랜잭션(Transaction)

트랜잭션(Transaction)이란, 데이터베이스 상태를 변화시키는 작업의 단위

❔데이터베이스 상태 변화

SELECT * FROM TODOLIST;
INSERT INTO TODOLIST VALUES(1, 'TODO');
UPDATE TODOLIST SET TODO = 'TODO2' WHERE ID = 1;
DELETE FROM TODOLIST WHERE ID = 1;

질의문(SQL)을 이용하여 데이터베이스에 접근하여 상태 변화를 준다.

 

트랜잭션의 작업 단위라는 것은, 사람이 정해둔 기준에 따라 구분되어 정해진다.

EX)

  1. 사용자가 새로운 TO-DO를 작성한다.
  2. 새롭게 작성된 TO-DO가 포함된 TO-DO-LIST가 보여진다.
INSERT INTO TODOLIST VALUES(1, 'TODO');
SELECT * FROM TODOLIST;

위의 두 문장을 합쳐 작업 단위로 묶어 표현하면, 이를 하나의 트랜잭션이라고 하게 된다.

따라서, 하나의 작업 단위인 트랜잭션을 잘 설계하는 것이 중요하다.

트랜잭션의 특징

원자성, 일관성, 독립성, 지속성

원자성(Atomicity)

트랜잭션이 데이터베이스에 모두 반영되거나, 혹은 모두 반영되지 않도록 해야 한다.

우리가 정해둔 작업 단위별로 작업이 이루어지지 않을 경우,

  • 기준이 흐트러진 상태이므로 오류를 해결하기 어려움
  • 데이터 처리가 어떻게 진행되었는지 파악하기 어려움

따라서, 반드시 정해둔 작업 단위별로 진행되어야 한다.

그렇기 때문에, 데이터베이스에 모두 반영될 수 없다면, 모두 취소되도록 하는 것이 안전한 방법이자 이를 원자성이라 한다.

일관성(Consistency)

트랜잭션의 작업 처리 결과가 항상 일관되어야 한다.

이미 진행되고 있는 트랜잭션이 있는 경우, 데이터베이스에 변경이 생긴다면?

작업이 진행 중이었던 트랜잭션은 데이터베이스에 변경이 이루어지더라도, 기존에 참조해오던 데이터베이스를 기준으로 꾸준히 작업을 진행한다. 데이터베이스가 중간의 변경되더라도 일관성 있게 참조해오던 데이터를 처리하도록 한다.

독립성(Isolation)

둘 이상의 트랜잭션이 실행되고 있는 경우, 어떠한 트랜잭션이라도 다른 트랜잭션이 연산에 끼어들 수 없다.

해당 트랜잭션이 완료될 때까지, 어떠한 참조도 불가능하다.

지속성(Durability)

트랜잭션의 작업이 완료되었다면, 해당 결과는 영구적으로 데이터베이스에 반영되어야 한다.

Commit & RollBack

Commit

데이터베이스에 모두 반영되어 원자성이 지켜지고, 처리 결과가 일관된 경우.

하나의 작업 단위인 트랜잭션이 완전히 완료된 상태, 즉 작업의 완료를 알리기 위해 Commit한다.

수행된 트랜잭션이 로그에 저장되며, 트랜잭션의 종료를 의미한다.

Rollback

데이터베이스에 모두 반영되지 않아 원자성이 깨지는 경우,

원자성을 지키기 위해 부분적으로 연산이 진행된 트랜잭션을 취소하고 처음부터 실행하고자 RollBack한다.

비정상적으로 종료된 트랜잭션을 모두 취소시켜버리는 것을 의미한다.

+ CheckPoint : Rollback의 지점을 설정

EX )

A, B, C에게 10만원을 입금하는 상황 (하나의 트랜잭션)

  • A, B에게 10만원 입금 성공
  • C에게 입금 실패

다시 트랜잭션이 그대로 실행된다면, A와 B에게 입금이 또 되어서 총 20만원이 보내지게 된다.

모든 입금을 취소하는 Rollback이 필요하다.

무사히 입금이 완료된 경우엔, Commit

트랜잭션의 상태

트랜잭션 상태

활동(Active) : 트랜잭션이 실행 중인, 작업 중인 상태

실패(Failed) : 트랜잭션 실행 중 오류로 인해 비정상적으로 중단된 상태

철회(Aborted) : 트랜잭션의 비정상적인 종료로 Rollback 연산을 수행한 상태

부분 완료(Partially Committed) : 트랜잭션의 마지막 연산까지 실행되었으나 아직 Commit 연산이 실행되지 않은 직전의 상태

완료(Committed) : 트랜잭션이 성공적으로 수행되여 Commit 연산까지 실행된 상태

@Transactional

@Transactional
public void Method {
	...
}

프레임워크에서 트랜잭션을 생성하여 메서드를 하나로 묶어서 실행하도록 한다. 예외 상황 발생 시, 모든 작업이 Rollback되도록 한다.

메서드뿐만 아니라, class 위에도 붙일 수 있다. class 내부에 있는 메서드들을 transaction화하게 된다.

 

Transactional 전파 레벨은 총 7가지

트랜잭션 전파레벨에는 총 7가지 종류가 있다. 스프링 문서에 적혀있는 내용을 바탕으로 한 번 살펴보자.

 

  • REQUIRED: Support a current transaction, create a new one if none exists. 
  • REQUIRES_NEW: Create a new transaction, and suspend the current transaction if one exists.
  • SUPPORTS: Support a current transaction, execute non-transactionally if none exists.
  • NOT_SUPPORTED: Execute non-transactionally, suspend the current transaction if one exists.
  • MANDATORY: Support a current transaction, throw an exception if none exists.
  • NESTED: Execute within a nested transaction if a current transaction exists, behave like REQUIRED otherwise.
  • NEVER: Execute non-transactionally, throw an exception if a transaction exists. 

스프링에서 기본(default)으로 제공하는 전파레벨은 REQUIRED이다. 우리가 @Transactional 어노테이션을 사용하면 이 트랜잭션의 전파레벨은 REQUIRED이다.

 

Propagation.REQUIRED

@Transactional(propagation = Propagation.REQUIRED)
public void doSomething() {
    ...
}
  • default 값이기 때문에 생략가능해당 메소드를 호출한 곳에서 별도의 트랜잭션이 설정되어 있지 않다면 트랜잭션을 새로 시작한다.(새로운 연결을 생성하고 실행한다.)예외가 발생하면 롤백이 되고 호출한 곳에도 롤백이 전파된다.
  • 만약, 호출한 곳에서 이미 트랜잭션이 설정되어 있다면 기존의 트랜잭션 내에서 로직을 실행한다.(동일한 연결 안에서 실행됨)
  • 부모 트랜잭션 내에서 실행하며, 부모 트랜잭션이 없을 경우 새로운 트랜잭션 생성

Propagation.REQUIRES_NEW

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void doSomeThing() {...}
  • Propagation.REQUIRES_NEW로 설정되어있을 때에는 매번 새로운 트랜잭션을 시작한다. (새로운 연결을 생성하고 실행)새로운 트랜잭션 안에서 예외가 발생해도 호출한 곳에는 롤백이 전파되지 않는다.
  • 즉, 2개의 트랜잭션이 완전 독립적으로 동작한다.
  • 만약, 호출한 곳에서 이미 트랜잭션이 설정되어 있다면(기존 연결이 있다면) 기존의 트랜잭션은 메소드가 종료할 때까지 잠시 대기 상태로 두고 자신의 트랜잭션을 실행한다.(부모 트랜잭션 상관X)

Propagation.NESTED

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

Propagation.MANDATORY

@Transactional(propagation = Propagation.MANDATORY)
public void doSomeThing() {...}
  • 부모 트랜잭션 내에서 실행되며, 부모 트랜잭션이 없을 경우 Exception이 발생한다.

Propagation.SUPPORT

@Transactional(propagation = Propagation.SUPPORT)
public void doSomeThing() {...}
  • 부모 트랜잭션이 존재하면 부모 트랜잭션으로 동작하고, 없을경우 non-transactional 하게 동작한다.

Propagation.NOT_SUPPORT

@Transactional(propagation = Propagation.SUPPORT)
public void doSomeThing() {...}
  • non-transactional 로 실행되며 부모 트랜잭션이 존재하면 일시 정지한다.

Propagation.NEVER

@Transactional(propagation = Propagation.NEVER)
public void doSomeThing() {...}
  • non-transactional 로 실행되며 부모 트랜잭션이 존재하면 Exception이 발생한다.

no-rollback-for : 롤백하지 않을 익셉션 타입

rollback-for : 롤백할 익셉션 타입

 

격리수준 4가지

  • READ_UNCOMMITED (level 0)
  • READ_COMMITED (level 1)
  • REPEATABLE_READ (level 2)
  • SERIALIZABLE (level 3)

격리 수준이 높아질수록 동시성(Concurrency) 높아지고 속도는 느려진다.

 

READ_UNCOMMITED (커밋되지 않는 읽기, level 0)

  • 트랜잭션 처리중 or 아직 commit되지 않은 데이터를 다른 트랜잭션이 읽는 것을 허용
  • 어떤 사용자가 A라는 데이터를 B로 변경하는 동안 다른 사용자는 B라는 아직 완료되지않은(Uncommitted 혹은 Dirty) 데이터 B를 읽을 수 있다.
  • Dirty read 발생
  • 다른 트랜잭션에서 처리하는 작업이 완료되지 않았는데도 다른 트랜잭션에서 읽을 수 있는 현상
  • READ_UNCOMMITED 격리수준에서만 발생

READ_COMMITED (커밋된 읽기, level 1)

  • dirty read 방지 : 트랜잭션이 commit 되어 확정된 데이터만을 읽도록 허용
  • A라는 데이터가 B로 변경되는 동안 다른 사용자는 해당 데이터에 접근할 수 없다.

REPEATABLE_READ (반복 가능한 읽기, level 2)

  • 트랜잭션이 완료될 때까지 SELECT 문장이 사용하는 모든 데이터에 shared lock이 걸린다.
    따라서, 다른 사용자는 해당 영역에 대한 데이터 수정이 불가능하다.
  • 선행 트랜잭션이 읽은 데이터는 트랜잭션이 종료될 때까지 후행 트랜잭션이 갱신하거나 삭제하는 것을 불허함으로써 같은 데이터를 두 번 쿼리했을 때 일관성 있는 결과를 리턴한다.
  • Phantom READ 발생
  • 트랜잭션이 데이터를 두 번 읽는다고 가정 할 때 where 절의 조건에 맞는 레코드가 추가로 생성될 때,
  • 새로운 "유령(phantom)"행이 나오지만 지원하지 phantom read를 지원하지 않으면 새로 생긴 행을 볼 수 없다.

SERIALIZABLE (직렬화 가능, level 3)

  • 완벽한 읽기 일관성 모드 제공(가장 엄격함)
  • 성능 측면에서 동시 처리성능이 가장 낮다.
  • 거의 사용되지 않는다.
  • 데이터의 일관성 및 동시성을 위해 MVCC(Multi Version Concurrency Control)를 사용하지 않음
  • 트랜잭션이 완료될 때까지 SELECT 문장이 사용하는 모든 데이터에 shared lock 걸리므로 다른 사용자는 영역에 해당하는 데이터에 대한 수정 입력이 불가능하다.