SEB_BE_43 / 23.02.28 회고

rse·2023년 2월 28일
0

코드스테이츠_BE_43

목록 보기
44/65

오늘

  • 트랜잭션

트랜잭션

예시
카페에서 손님이 커피 2잔을 담고 결제버튼을 눌렀다. 그런데 웹 오류가 발생해 결제가 안됬지만 데이터베이스에는 커피 2잔이 담겨버리고 손님의 스탬프도 2개가 찍힌 것이다.

이렇게 되어버리면 카페 주인은 손해를 보게 된다.

또, 손님이 결제 버튼을 눌렀을 때 데이터베이스에서 오류가 발생해 결제는 성공적으로 되었지만 결과적으로 안시킨게 된다면 손님도 손해를 볼 것이다.

현재 손님이 커피를 주문하는 과정은 두개의 작업이 동시에 이루어져야 한다.
1. 손님의 결제
2. 데이터베이스에 추가.

이렇게 두개의 작업이 한개의 그룹처럼 처리되는 과정 중 둘 중 한개라도 처리에 실패할 경우 실패로 되는 것. 두개 다 성공해야 성공이 되는 것.

트랜잭션은 All or Nothing 의 처리 방식을 사용한다.
= 전부 성공하던지 전부 실패하던지.

그래서 트랜잭션이란?

데이터베이스의 상태를 변환시키는 하나의 논리적 기능을 수행하기 위한 작업의 단위 또는 한꺼번에 모두 수행되어야 할 일련의 연산들을 의미한다.

여기서 상태를 변환시킨다는 의미는
SQL 질의어를 통해 DB에 접근한다는 의미이다.

SELECT
INSERT
UPDATE
DELETE

그리고 한꺼번에 모두 수행되어야 할 일련의 연산은
위 예시처럼 두개의 기능이 한개의 그룹으로 묶어서 동시에 처리되야하는 연산을 말한다.

왜 사용하는가?

트랜잭션은 DB 서버에 여러 개의 클라이언트가 동시에 액세스 하거나 응용프로그램이 갱신을 처리하는 과정에서 중단될 수 있는 경우 등 데이터 부정합을 방지하고자 할 때 사용한다.

부정합이 발생하지 않으려면 프로세스를 병렬로 처리하지 않도록 하여 한 번에 하나의 프로세스만 처리하도록 하면 되는데, 이는 효율이 너무 떨어지기 때문에, 병렬로 처리할 수 밖에 없는 현실적인 상황으로 인해 부정합을 방지하고자 트랜잭션을 사용하는 것이다.

ACID 원칙

A (Atomicity)

= 원자성

작업을 더 이상 쪼갤 수 없음을 의미한다.

위 예제에서 커피 주문 작업, 스탬프 횟수 증가 작업은 필수로 해줘야하는 작업이다.
절대로 해줘도 되고 안해줘도 되는 작업이 아니다.
이런 식으로 쪼개서 처리 할 수 없는 작업.

하나의 작업으로 인식해서 둘 다 성공하던지 둘 다 실패하던지 (AII OR NOTHING) 중에서 하나로만 처리되는 것이 보장되어야 한다.

C (Consistency)

= 일관성

트랜잭션이 성공적으로 종료 될 경우, 비스니스 로직에서 의도한대로 일관성 있게 저장되거나 변경되는 것을 의미한다.

예시로 말하자면 커피 3잔을 주문했을 때 스탬프가 3개 찍혀야 정상이지만 4개가 찍힌다던지 적게 찍혔을 경우 일관성이 깨진다.

I (Isolation)

= 격리성

여러개의 트랜잭션이 실행 될 경우 각각 독립적으로 실행되어야 한다.
즉 하나의 트랜잭션 실행 시 다른 트랜잭션의 작업이 끼어들지 못하도록 보장하는 것.

그리고 하나의 트랜잭션 작업 중일때 다른 트랜잭션이 결과를 알 수 없다.

예제처럼 결제에 실패하거나 문제가 생겼을 경우 결과가 실패로 되는데 다른 트랜잭션은 이 결과를 트랜잭션이 종료되기 전까지 알 수 없다는 의미이다.

D (Durability)

= 지속성

트랜잭션이 완료되면 그 결과는 지속되어야 한다는 의미.

데이터베이스가 종료되어도 데이터는 계속 살아있어야 한다는 의미이다.

예제로 보면 고객이 결제를 완료하고 데이터베이스에도 추가가 되었는데 정전이 일어나서 기계가 꺼져버려도 고객이 주문한 음료와 스탬프는 사라지지 않아야 한다는 의미이다.

Commit / Rollback

트랜잭션에서 제일 중요한건 commit과 rollback인데
아까 위에서 얘기한 둘 다 성공하던지 둘 다 실패하던지 (AII OR NOTHING)의 처리방식에서
성공을 하면 commit을 해주고 실패를 하면 rollback을 해준다.

Commit

  • 모든 작업을 최종적으로 DB에 반영하는 명령어. 수행하면 변경된 내용이 데이터베이스에 영구적으로 저장됨.
  • commit 명령을 수행하지 않을 시 DB에 최종적으로 반영안됨.
  • commit 명령을 수행하면 하나의 트랜잭션 과정은 종료.

Rollback

  • 작업 중 문제가 발생했을 때, 트랜잭션에서 수행하고 있던 작업을 취소한다.
  • rollback 명령 수행 시 트랜잭션 시작 전으로 되돌아간다.

실제 예시를 보자.

Commit 예시

두개를 insert 로 넣었지만 commit은 하나만 했기 때문에 조회 결과가 하나만 나오는 것을 알 수 있다.

Rollback 예시

실행 결과를 보면 아무것도 저장되지 않은 것을 볼 수 있다.

이걸 코드에서는 어떻게 사용할까?

코드에서는 EntityManagerFactory에서 Transaction을 얻고,
commit 메서드를 호출 해 반영하고 있다.

Spring Transaction

선언형 방식(애너테이션 방식)

@Transactional 이라는 애너테이션을 트랜잭션이 필요한 영역에 추가해 주는 것.

이런식으로 클래스 레벨에 추가하면 해당 클래스에서 MemberRepository의 기능을 이용하는 모든 메서드에 트랜잭션이 적용된다.

실행시키고 Postman에서 PostMember 핸들러 메서드를 호출했을 때 콘솔에 출력되는 로그 중 일부.

맨 위에 Creating new transaction with name ~ 은 조금 잘렸지만 MemberService에 created 메서드가 호출되면서 트랜잭션이 생성되고 있다는 것을 알 수 있다.

그리고 Commiting JPA treansection an EntityManager ~ 부분은 트랜잭션 커밋이 되고 있다는 것이고

바로 밑 줄에 Not closing pre-bound JPA EntityManager after transaction 은 트랜잭션이 종료되었다는 의미다.

그리고 맨 밑 줄은 EntityManager 를 종료하고 있다.

이번에는 Rollback 을 해보자.

Initiating transaction rollback 부분, 바로 밑 줄인 Rolling back JPA ~ 부분에서 rollback 이 정상적으로 작동하는 것을 알 수 있다.

메서드 위에 Transactional 애너테이션을 붙이고 readOnly 라고 추가하면 그 메서드는 읽기 전용 트랜잭션이 된다.

조회 메서드에 readOnly = true 로 설정하는 이유

readOnly = true로 설정해도 commit 절차를 진행하긴 한다.

위에 사진에도 Initiating transaction commit 이라고 콘솔창에 출력이 되어있다.
JPA에서 commit이 호출되면 영속성 컨텍스트가 flush 된다.

flush란 = 영속성 컨텍스트의 변경 내용을 DB에 동기화하는 것을 말한다.

하지만 readOnly = true 로 설정하면 JPA 내부적으로 영속성 컨텍스트를 flush 하지 않는다.
그리고 읽기 전용 트랜잭션일 경우, 변경 감지를 위한 스냅샷 생성도 하지 않는다.

즉, 불필요한 추가 동작을 줄일 수 있다는 의미!

class 와 mathod 의 트랜잭션 적용 순서

  • 클래스 레벨에만 @Transaction 애너테이션을 적용했을 경우
    클래스 레벨의 @Transaction 애너테이션이 메서드에 일괄 적용된다.

  • 클래스와 메서드 동시에 적용했을 경우
    메서드 레벨의 @Transaction 애너테이션이 먼저 적용된다.
    그리고 만약 메서드 레벨에 @Transaction 애너테이션이 적용되지 않았을 경우, 클래스 애너테이션이 적용된다.

여러 작업이 하나의 트랜잭션에 묶이는 경우

propagation.REQUIRED 를 지정하면 메서드 실행 시, 현재 진행 중인 트랜잭션이 존재하면 해당 트랜잭션을 사용하고, 존재하지 않으면 새 트랜잭션을 생성하도록 해준다.

orderController에 post를 하니 이렇게 트랜잭션이 rollback 된 것을 볼 수 있다.

트랜잭션 전파 (Transaction Propagation)

종류
Propagation.REQUIRED

일반적으로 가장 많이 사용되는 Propagation 유형의 디폴드값이다.
진행 중인 트랜잭션이 없으면 새로 시작하고, 있으면 해당 트랜잭션에 참여한다.

Propagation.REQUIRES_NEW

진행중인 트랜잭션과 무관하게 새로운 트랜잭션이 시작된다.
기존에 진행중이던 트랜잭션은 새로 시작된 트랜잭션이 종료할 때까지 중지된다.

Propagation.MANDATORY

진행중인 트랜잭션이 없으면 예외를 발생시킨다.

Propagation.NOT_SUPPORTED

트랜잭션을 필요로 하지 않음을 의미한다. 
진행 중인 트랜잭션이 있으면 실행이 종료될 때 까지 진행중인 트랜잭션은 중지되며, 
메서드 실행이 종료되면 트랜잭션을 계속 진행한다.

Propagation.NEVER

트랜잭션을 필요로 하지 않음을 의미하며, 진행 중인 트랜잭션을 존재할 경우, 예외를 발생.

이 외에도 Propagation 유형이 있다.

profile
기록을 합시다

0개의 댓글