본문 바로가기
Computer Science/데이터베이스

[DB] 트랜잭션 (Transaction)

by 어썸오184 2021. 8. 24.
728x90
반응형

트랜잭션이란

트랜잭션이란 데이터베이스의 논리적인 작업의 단위를 말한다. 하나의 트랜잭션은 여러 쿼리(연산)로 구성되는데, 데이터베이스의 일관성을 유지하기 위해 트랜잭션은 아래 네 가지 특성을 가지며 이를 흔히 ACID라고 부른다.

  • Atomicity : 하나의 트랜잭션 안에 있는 쿼리는 모두 반영되거나(커밋) 모두 반영되지 않아야(롤백) 한다.(All or nothing)
  • Consistency: 데이터가 트랜잭션 실행 이후에도 일관성을 유지해야한다.
  • Isolation: 트랜잭션은 서로 격리되어야 한다. 즉 하나의 트랜잭션이 다른 트랜잭션의 결과에 영향을 미쳐서는 안된다. 가장 좋은 방법은 모든 트랜잭션을 순차적으로 실행하는 것이다. 그러나 성능 향상을 위해서는 트랜잭션이 병렬적으로 수행될 필요가 있는데, 이를 위해 DBMS는 트랜잭션을 병렬적으로 수행하면서도 데이터베이스의 일관성을 지킬 수 있도록 여러 격리 수준을 제공한다.
  • Durability: 트랜잭션이 성공적으로 실행됐다면 그 결과는 시스템에 영속적으로 보존되어야 한다.

트랜잭션의 필요성을 보여주는 가장 대표적인 예가 송금 거래이다. 계좌 A에서 계좌 B로 10000원을 송금할 때, 일어나는 연산은 다음과 같다.

UPDATE accounts
SET balance = balance - 10000
WHERE name = 'A';

UPDATE accounts
SET balance = balance + 10000
WHERE name = 'B';

위 두 개의 연산은 하나의 트랜잭션으로 묶여서 원자적으로 처리되도록 보장되어야 한다. 예를 들어 A 계좌에 UPDATE를 친 직후에 시스템에 문제가 생겨 세션이 종료되었다면 해당 연산은 무효로 처리되어야 한다. 그렇지 않으면 계좌 B의 잔액은 그대로인데 계좌 A의 잔액만 10000원이 줄어있는 문제가 생긴다.

만약 트랜잭션이라는게 없다면, 어플리케이션이 직접 데이터의 일관성을 보장하는 처리를 해줘야할 것이다.

REDO, UNDO

출처: 데이타베이스 시스템 개정판 : 정익사

트랜잭션의 일관성을 지키기 위해서 DBMS는 로그 파일을 따로 보존하며 로그 파일에는 데이터베이스에서 일어난 모든 변화가 기록되어있다. 트랜잭션의 관리는 이 로그 파일의 기록을 바탕으로 이루어진다(일반적으로). 트랜잭션의 비정상적인 종료를 회복하기 위해서 DBMS는 REDOUNDO 를 사용하는데, 이는 해당 DBMS의 버퍼 관리 정책에 따라 다르게 적용된다.

어떤 이유로든 트랜잭션을 정상 종료할 수 없을 때, 해당 트랜잭션이 변경한 테이블은 트랜잭션 이전 상태로 복구되어야 한다. 이를 UNDO라고 한다.

REDO는 UNDO의 반대 개념으로 이미 커밋한 트랜잭션의 수정을 재반영하는 연산이다. 만약 페이지 버퍼가 커밋 시점에 변경 사항을 모두 디스크에 반영한다면 REDO가 필요 없다. 하지만 거의 대부분의 DBMS는 효율성을 위해 트랜잭션이 커밋되더라도 일정시간동안 이를 디스크에 반영하지 않고 버퍼에만 저장하고 있는데, 만약 이 시점에 문제가 생긴다면 REDO를 통해 수정 사항을 재반영 해주어야한다.

로그 레코드는 갱신 이전 이미지와 이후 이미지를 모두 다 가지고 있으며, UNDO 복구 때에는 이전 이미지로 현재 이미지를 대체하며, REDO 복구 때에는 이후 이미지를 반영하는 방식으로 복구가 이루어진다.

트랜잭션 전파

트랜잭션의 범위는 커넥션이다. 무슨 말이냐면 만약 하나의 트랜잭션에서 다른 메서드를 호출할 때, 해당 메서드에서 새로운 커넥션을 얻어 작업을 진행한다면 이는 다른 트랜잭션으로 관리되므로 롤백이 진행됐을 때 결과가 예상과 다를 수 있다.

출처: 최범균 DB 트랜잭션 이해하기

위 그림에서 6번 롤백이 실행되어도 4.x는 다른 트랜잭션이기 때문에 롤백 처리가 되지 않는다. 따라서 여러 메서드를 호출할 때 다양한 메서드를 하나의 트랜잭션으로 묶고 싶다면, 여러 메서드에서 하나의 커넥션을 사용할 수 있는 방법을 생각해야한다. 이를 트랜잭션 전파라고 한다. 하지만 우리가 직접 방법을 강구할 필요는 없다. 보통 프레임워크에 이미 트랜잭션 전파가 구현되어 있기 때문이다.

트랜잭션 전파: 여러 메서드가 하나의 트랜잭션으로 묶이기 위해 필요, 보통 프레임워크에 구현되어 있다.

예를 들어, 스프링의 @Transactional 애너테이션을 사용하면 따로 메서드 간에 커넥션을 전달하지 않아도 모든 작업이 하나의 트랜잭션으로 묶여서 처리된다.

@Transactional
public void doSomething() {
    dao.methodA();
    ...
    dao.methodB();
}
public void methodA() {
    jdbcTemplate.update(...);
}

public void methodB() {
    jdbcTemplate.update(...);
}

 

하지만 만약 트랜잭션 내에 외부 API와의 연동이 있다면 롤백 처리에 주의해야한다.

내 애플리케이션에서 롤백 처리를 하더라도 내가 외부 API를 호출함으로써 변경된 사항은 그대로 반영이 되기 때문이다. 따라서 이런 경우에는 롤백 후 외부 시스템의 상태를 원복하는 방법에 대해 고민해야한다.

 

참고자료

728x90
반응형

댓글