[SEB BE] Section 3. 트랜잭션(Transaction)?

박두팔이·2023년 2월 28일
0

스프링프레임워크

목록 보기
11/18

트랜잭션?

여러개의 작업들을 하나의 그룹으로 묶어서 처리하는 처리 단위 (All or Nothing)

트랜잭션을 가장 간단히 설명할 수 있는 것 중 하나가 '계좌이체'이다.
A라는 사람이 B에게 송금을 하였는데 A계좌에서 출금이 된 이후 B계좌로 이체되기 직전에 데이터베이스에 문제가 생겨 저장이 되지 않았다고 해보자. 이런경우 A는 통장에서 금액이 인출되었지만 B는 받지 못했다. 결국 그 돈은 증발해버린 것이다.

이러한 신뢰성이 깨지는 상황을 막기위하여 여러개의 작업을 하나의 작업으로 인식하여 전부 성공하던지, 전부 실패하던지의 방식으로만 처리하는 '트랜잭션'의 단위가 생겨난 것이다.

  • All or Nothing이라는 트랜잭션 처리 방식은 데이터의 무결성을 보장하는 핵심적인 역할을 한다.

ACID원칙

트랜잭션의 특징을 설명하기 위해서는 일반적으로 ACID 원칙이 필요하다.

1. 원자성(Atomicity)

더이상 작업을 쪼갤 수 없음

👉🏻 여러개의 작업을 논리적으로 하나의 작업으로 인식하여 모두 성공하거나, 모두 실패하는 처리방식을 보장한다.


2. 일관성(Consistency)

트랜잭션이 에러없이 성공적으로 종료되었다면 비즈니스 로직이 실행될 때 저장되거나 변경사항에 일관성을 갖는다.

👉🏻 3잔의 커피를 주문했다면 스탬프의 횟수도 동일하게 +3이 증가되어야 한다는 것이 일관성의 예이다.


3. 격리성(Isolation)

여러개의 트랜잭션이 실행될 경우 독립적으로 실행되어야한다.

👉🏻 격리성을 이해하기 위해선 CPU가 프로세스를 처리하는 과정을 이해하는 것이 좋다. 컴퓨터로 음악을 들으며 워드작업을 하고 있다고 가정할 때 우리 눈에는 보이지 않지만 CPU는 음악과 워드작업 두 가지 프로세스를 아주 빠른 속도로 번갈아가며 실행시키고 있다.

데이터베이스 역시 성능향상을 목적으로 한 개 이상의 트랜잭션을 번갈아가면서 처리할 수 있다. 그럼에도 각각의 트랜잭션이 다른 트랜잭션에 영향을 주지 않고 독립적으로 실행이 된다는 것이 격리성이다.


4. 지속성(Durability)

트랜잭션이 완료된 후 그 결과는 지속되어야 한다.

👉🏻 데이터베이스가 종료되어도 데이터는 물리적인 저장소에 저장되어 지속적으로 유지되어야 한다.


트랜잭션 커밋과 롤백

커밋(commit)

모든 작업이 데이터베이스에 영구적으로 저장된다.

  • commit명령을 수행하지 않으면 데이터베이스에 최종적으로 반영되지 않는다.
  • commit명령을 수행하면 하나의 트랜젝션은 종료된다.
BEGIN TRANSACTION;
insert into  MEMBER VALUES 
       (1, now(), now(), 'hgd1@gmail.com', 'MEMBER_ACTIVE', '홍길동1', '010-1111-1111');
COMMIT;

BEGIN TRANSACTION;
insert into  MEMBER VALUES 
       (2, now(), now(), 'hgd2@gmail.com', 'MEMBER_ACTIVE', '홍길동2', '010-2222-2222');

예를들어 이러한 쿼리문을 실행했다고 한다면, 결과는 1개의 row만 저장된다. 왜냐하면 2번째 row는 commit을 하지 않았기 때문이다.

롤백(rollback)

문제발생 시, 트랜잭션 내에서 수행된 작업을 모두 취소한다.

  • 트랜잭션 시작 이전의 상태로 되돌린다.

H2 웹 콘솔에서 rollback 예시를 살펴보자.

BEGIN TRANSACTION;
insert into  MEMBER VALUES 
			(1, now(), now(), 'hgd1@gmail.com', 'MEMBER_ACTIVE', '홍길동1', '010-1111-1111');
ROLLBACK;
COMMIT;

위의 코드를 실행시키면 INSERT문이 실행되었지만 commit직전에 rollback명령을 먼저 수행했기 때문에 트랜잭션의 상태가 INSERT문을 실행하기 이전 상태로 되돌아 갔다. 따라서 INSERT문에서 실행된 데이터는 테이블에 저장되지 않았다.


JPA의 commit()이 실행되는 동작 과정

위의 그림은 JPA API를 통해 엔티티트랜잭션 객체를 얻은 후 앤티티트랜잭션 객체로 commit()메서드를 호출하는 과정이다.

  • JPA 기술을 사용한 데이터베이스와의 인터랙션은 내부적으로는 JDBC API를 통해서 이루어진다.

1️⃣ TransactionImpl

package org.hibernate.engine.transaction.internal;

public class TransactionImpl implements TransactionImplementor {
			...
			...
			@Override
			public void commit() {
				...
				...
				try {
					internalGetTransactionDriverControl().commit(); // (1)
				}
				catch (RuntimeException e) {
					throw session.getExceptionConverter().convertCommitException( e );
				}
			}
}

위의 코드는 TransactionImpl클래스의 일부 코드이다.

JPA 엔티티트랜잭션 객체로 .commit()메서드를 호출하는 것은 엔티티트랜잭션 인터페이스의 구현클래스인 TransactionImpl클래스의 commit()을 호출하는 것이다.

TransactionImpl 클래스에서는 위의 코드와 같이 물리적인 트랜잭션을 제어하기 위한 로컬 트랜잭션 드라이버 구현 객체를 얻은 후에 구현 메서드인 commit()을 다시 호출한다.


2️⃣ JdbcResourceLocalTransactionCoordinatorImpl > TransactionDriverControlImpl

3️⃣ AbstractLogicalConnectionImplementor

4️⃣ JdbcConnection

5️⃣ Command


트랜잭션은, 두 가지로 구분할 수 있다.

  • 로컬 트랜잭션
  • 분산 트랜잭션

Spring에서 사용되는 트랜잭션 방식 2가지

  • 선언형 트랜잭션 방식
    • 비즈니스 로직에 애너테이션을 추가하는 방식
    • AOP방식을 이용하여 비즈니스로직에서 트랜잭션 적용 코드 자체를 감추는 방식
  • 프로그래밍 코드 베이스 트랜잭션 방식

이와 관련된 내용은 뒤에서 설명하기로 한다.

profile
기억을 위한 기록 :>

0개의 댓글