Transaction

전우재·2024년 2월 11일

Study

목록 보기
3/4

배경

  • A와 B의 거래를 한다고 가정하자.
UPDATE accounts
SET balance = balance - 1000
WHERE member = ‘A’;

UPDATE accounts
SET balance = balance + 1000
WHERE member = ‘B’;
  • 쿼리를 다음과 같이 작성하여 처리할 수 있다.
  • 하지만, 두 쿼리를 처리하는 도중에 에러가 발생한다면 어떻게 될까?

Transcation

  • 데이터베이스의 상태를 변경시키기 위한 일련의 단위
  • 여러 읽기/쓰기를 하나로 묶어줌
  • 트랜잭션으로 묶었을 때 성공한 경우
  • 트랜잭션이 시작하고 원하는 작업들을 수행한 후, 문제가 없었다면 Commit을 통해 DB에 반영한다.

  • 트랜잭션으로 묶었을 때 실패한 경우
  • 트랜잭션이 시작하고 문제가 발생한다면, 트랜잭션 시작 이후 발생한 작업들에 대해 Rollback하여 DB에 반영하지 않는다.

Transaction 동작 과정

  • 앞에서 말한 Transcation이 실제로 어떻게 동작하는지 찾아봤다.
  • 기존 데이터베이스에 100, 200이라는 값이 저장되어 있다고 가정하자.
  • 해당 값들을 update하는 작업을 transcation으로 수행할 것이다.
  • 자주 사용하는 mysql을 예시로 한다.

Transaction 시작


1. 여러 쿼리들을 transcation으로 묶기 위해서는 쿼리 시작 전에 transaction을 시작한다고 쿼리를 작성해야 한다.
2. 그럼, 로그파일에 기존 체크포인트 이후 쿼리 내용들이 기록되기 시작한다.

Update Data


1. trnsaction이 시작하고, 데이터를 조작할 쿼리가 진행된다.
2. update를 수행하기 위해 실제 데이터 값을 데이터 캐시로 불러온다.
3. 불러온 데이터 캐시에 값을 변경한다.
4. 변경한 내용은 아직 Commit하지 않았기 때문에 실제 내용에는 적용되지 않는다.
5. 변경을 위해 수행한 작업들은 로그에 기록된다.

Commit Transaction


만약 트랜잭션 작업 도중에 문제가 없었다면

  • Transcation으로 묶었던 작업의 결과를 실제 DB에 반영해야한다.
  1. 트랜잭션 내용을 Commit하여 Commit한 내용도 로그 파일에 기록한다.
  2. 데이터 캐시에서 변경한 내용을 실제 데이터에 덮어쓴다.
  3. 로그에 새로운 checkpoint가 만들어 지며 언제든 해당 지점으로 데이터를 복구할 수 있다.
  • 그럼 트랜잭션 작업 도중에 문제가 있었다면 어떻게 될까?

Transcation 상태 다이어그램

  • 앞에서 설명한 대로 Transction은 여러 작업을 하나로 묶는다.
  • 따라서, commit이 완료된 것이 아니라면 그 전까지의 작업은 수행되지 않은 것과 같아야 한다.
  • commit되기 전까지 사용자가 요청한 쿼리 및 쿼리 요청이 오면 해당 트랜잭션은 patially committed상태가 된다.
  • 문제가 없었다면 committed로 완료가 되지만, 문제가 생긴다면 failed 상태를 거쳐 aborted 상태가 된다.

Transaction 철회

표 출처 - 오라클 10g 교재 9-8

UndoRedo
기록하는 내용되돌리기 위한 변화. 트랜잭션이 가한 변경사항을 로깅재생하기 위한 변화. 데이터베이스의 변경 사항을 로깅
용도롤백, 읽기 일관성복구
저장소undo 세그먼트redo 로그 파일
보호하는 것유저의 읽기 일관성을 보호데이터의 손실 방지
  • 트랜잭션이 실패하여 transation을 철회하는 경우 undo 복구를 수행한다.
  • undo 복구 작업
    1. 작업해온 transcation 별로 할당된 undo 세그먼트에 로그가 저장되어 있다.
    2. 이것을 반대로 바꿔주는 작업을 한다. ex) 앞에서 100에서 1로 데이터를 변경했다면 다시 100으로 바꿔주는 작업
  • 해당 작업을 통해 transaction으로 묶은 작업이 모두 진행되거나 진행되지 않게 할 수 있다.
  • undo와 비슷한 redoundo와 반대로 했던 작업을 그대로 수행하는 것이다.
  • redo 복구 작업 : 데이터베이스가 비정상적으로 종료되었을 때, 데이터베이스를 복구하는 용도로 사용된다.
  • 그럼 여러 transaction이 철회될때는 어떻게 처리될까?

여러 트랜잭션이 겹쳐 있을 때

  • transaction의 범위는 connection을 기준으로 한다. 이것을 잘모 이해하면 코드에 실수가 발생할 수 있다.
  • 그림의 사황을 예로 살펴보자.
    • 멤버 서비스에서 db 데이터를 수정하기 위해 connection을 생성하여 transaction작업을 수행했다.
    • transcation을 시작해서 sql문도 execute하여 보낸다.
    • 이후에 post도 db 작업을 수행하기 위해 포스트 서비스 내부 메서드를 호출한다.
    • 해당 작업 이후에 문제가 생겨서 rollback을 하게 된다.
  • 이때 로직이 기대하는 작업은 무엇일까?
  • 보통 멤버와 포스트 작업 모두 수행하기 전으로 돌아가길 원할 것이다.
  • 하지만 커넥션 2를 보면 이미 commit이 되었기 때문에 돌아가지 않을 것이다.
  • 이렇게 connection에 따라 다르게 동작할 수 있다는 것을 인식하고, 실제로 개발할 때는 여러 메서드를 호출할 때 하나의 transaction으로 묶거나 분리하면 된다.

코드로 transaction 다루기

public void writePost(String memberId, String title) {
   try {
       Connection connection = ...;
       connection.setAutoCommit(false);
       ..
       // 서비스 로직
       postService.writeNewPost(connection, title);
       ..
       connection.commit();
   } catch (Exception e) {
       ...
       connection.rollback();
   } finally {
       connection.setAutoCommit(true);
   }
}
  • 기본 java에서 여러 트랜잭션을 묶으려면 다음과 같이 작업을 해야한다.
    • DB 서버와 connection을 맺어 객체를 생성
    • 오토커밋 옵션 끄기
      • 이 오토커밋을 끄지 않으면 해당 서버에서 쿼리나 작업을 할 때마다 자동으로 commit되어 transaction을 사용할 수 없다.
      • 따라서 오토커밋 옵션을 끄고 키는 것이 transaction을 시작하고 종료한다는 것을 명시하는 것과 비슷하게 생각할 수 있다.
  • transaction시작 선언 이후 다른 서비스의 트랜잭션 작업을 하는 메서드를 호출한다.
    • 만약 하나의 transaction으로 적용하려면 만들어진 커넥션 객체를 넘겨주고 로직을 진행한 후 해당 객체로 commit을 수행하면 된다.
    • 만약 transaction을 분리하여 적용하려면 다른 서비스의 메서드에서 커넥션을 새로 맺으면 된다.

하지만 현재 코드에서는 내부 서비스 로직을 다루는 부분과 트랜잭션 설정을 다루는 부분이 함께 있다.

  • 때문에 코드를 보기 불편하고, transaction작업을 위해 같은 코드를 반복해서 작성해야 하는 Boiler Plate 코드가 발생한다.
  • Spring에서는 해당 문제점을 위해 @Transactional을 사용하여 트랜잭션 설정하는 부분을 숨길 수 있다.
@Transactional
public void writePost(String memberId, String title) {
       ..
       // 서비스 로직
       postService.writeNewPost(connection, title);
       ..
       
}
  • 앞에서 trasaction이 동작중인데 다른 transaction을 실행한 경우 어떻게 처리하는지에 대한 개념을 spring에서는 트랜잭션 전파라고 다루고 있다.
  • spring의 경우 @Transactional에서 속성 종류를 변경하여 내부에서 생겨난 transaction을 어떻게 처리할지 설정할 수 있다.

외부 API 트랜잭션 처리

  • 만약 transaction 작업 내에 외부 api 작업이 있을때도 주의해야한다.
  • transaction작업 도중에 외부 api 호출이 있은 후, insert 작업을 처리하다가 오류가 생기는 상황이 발생했다.
  • 현재 서버에서 작업한 내용은 rollback할 수 있겠지만, 외부 api를 호출했던 외부 서버트랜잭션을 보장할 수 없을 것이다.
  • 지금은 작업 순서를 변경하여 해결할 수 있겠지만 api 작업 중 에러가 발생하거나 순서를 변경하기 힘든 경우가 있어 문제가 될 것이다.
  • 해당 문제를 해결하려면 외부 시스템에도 상태를 복구할 수 있도록 하는 방법에 대한 고민이 필요하다.
    • 여기서 이벤트나 비동기 메시징으로 외부 서버에 대해서도 트랜잭션을 처리하는 방법을 고민할 수 있다.

ACID

정리하면 transaction은 다음과 같은 성질들을 가진다.

1. 원자성(Atomicity)

2. 일관성(Consistency

3. 독립성(Isolation)

4. 영속성(Durability)

참조
https://www.youtube.com/watch?v=urpF7jwVNWs
https://d2.naver.com/helloworld/407507

0개의 댓글