DB : ACID와 트랜잭션

ysng_is_yosong·2024년 1월 18일

CS

목록 보기
3/6
post-thumbnail

DB: ACID와 트랜잭션

Transaction이란?

Ex) 계좌이체

J가 100만원이 있고 H가 200만원이 있을 때 J가 H에게 20만원을 이체하려는 경우

아래와 같은 두 개의 update문이 필요할 것이다

1. UPDATE account SET balance = balance - 200000 WHERE id = 'J'
2. UPDATE account SET balance = balance + 200000 WHERE id = 'H'

만일, 1번 업데이트문만 실행되고 2번이 실행되지 않는다면 갑자기 20만원은 J의 계좌에서 사라진 것이 된다.

만일, 2번 업데이트문만 실행되고 1번이 실행되지 않는다면 갑자기 20만원은 H의 계좌에 생겨난 것이 된다.

즉, 이 업데이트 문들은 둘 다 정상 처리되어야만 성공하는 ‘단일 작업’이 되어야 한다.

이를 데이터 베이스에서는 Transaction이라고 부른다.

트랜잭션은

  1. 단일한 논리적인 작업단위(a single logical unit of work)
  2. 논리적인 이유로 여러 SQL문들을 단일 작업으로 묶어서 나눠질 수 없게 만든 것
  3. Transaction 안에서 SQL문들 중에 일부만 성공하게 된다면 DB에 반영되지 않는다.

MySQL에서의 트랜잭션 예시

정상적으로 이체가 되는 경우

J가 H에게 20만원을 이체하는 것을 MySQL의 트랜잭션으로 확인하면 아래와 같다.

  • COMMIT이란? 지금까지 작업한 내용을 DB에 영구적(permanently)으로 저장 저장 후 Transaction을 종료

아래와 같이 정상적으로 이체가 된 것을 확인 할 수 있다.

중간에 ROLLBACK을 수행하는 경우

J가 H에게 추가로 30만원을 더 이체한다고 해보자

  • ROLLBACK이란?

지금까지 작업들을 모두 취소하고 Transaction이 시작되기 이전 상태로 되돌린다.

되돌린 후 Transaction을 종료한다.

아래와 같이 ROLLBACK을 수행하게 되면 J의 계좌가 80만원으로 복구된 것을 볼 수 있다.

AUTOCOMMIT이란?

  • 각각의 SQL문을 자동으로 Transaction 처리해주는 방식
  • SQL문을 성공적으로 실행하면 자동으로 COMMIT 한다.
  • 만일 로직을 실행 중에 문제가 있다면, 알아서 ROLLBACK 한다.
  • MySQL에서는 Default로 AUTOCOMMIT이 Enable 되어있다.
  • 다른 DBMS에서도 같은 기능을 제공해준다.
SELECT @@AUTOCOMMIT;

위의 명령어는 현재 AUTOCOMMIT이 활성화된 상태인지 아닌지 확인하는 쿼리이다.

만일, AUTOCOMMIT이 활성화된 상태에서 아래의 쿼리를 실행하면

INSERT INTO account VALUES ('W', 1000000);

INSERT문 실행 후 자동으로 COMMIT이 되고 account 테이블에 영구적으로 저장된다.

만일, AUTOCOMMIT을 다음과 같이 비활성화 해보자.

SET AUTOCOMMIT = 0;

이 상태에서 100만원 이하의 계좌는 모두 삭제한다는 쿼리를 날려보자

DELETE FROM account WHERE balance <= 1000000;

그러면 아래와 같이 H의 계좌만 남아있게 된다.

하지만 현재 AUTOCOMMIT을 꺼놓은 상태이다. 이 상태에서 ROLLBACK을 하게 된다면 다시 이전 상태로 돌아갈 수 있는 것이다.

위 처럼 ROLLBACK을 수행하게 되면 삭제됐던 계좌가 다시 복구된다.

MySQL에서 TRANSACTION과 AUTOCOMMIT의 관계

  • MySQL에서 START TRANSACTION을 실행하게 되면 AUTOCOMMIT은 자동으로 off가 된다.
  • COMMIT/ROLLBACK과 함께 TRANSACTION이 종료되면 원래 AUTOCOMMIT 상태로 돌아간다.

만일, TRANSACTION이 시작되기 전에 AUTOCOMMIT이 켜져있는 상태라면 COMMIT/ROLLBACK 후에 AUTOCOMMIT은 다시 켜지게 된다.

TRANSACTION이 시작되기 전에 AUTOCOMMIT이 꺼져있는 상태라면 COMMIT/ROLLBACK 후에도 AUTOCOMMIT은 꺼져있게 된다.

다른 RDBMS도 비슷하게 동작한다.

일반적인 Transaction의 사용패턴

  1. Transaction을 시작(begin)한다.
  2. 데이터를 읽거나 쓰는 등의 SQL문들을 포함해서 로직을 수행한다.
  3. 일련의 과정들이 문제 없이 동작했다면 Transaction을 Commit한다.
  4. 중간에 문제가 발생했다면 Transaction을 Rollback한다.

개발을 할때는 SQL이 아닌 프로그래밍 언어로 다루기 때문에 아래와 같이 진행된다.

  1. DB서버와 커넥션을 맺는다.
  2. AUTOCOMMIT을 False로 만든다 → 트랜잭션을 시작하겠다는 의미
  3. 로직을 구현
  4. 예외가 발생하지 않았다면 커밋을 하고 예외가 발생하면 롤백 시킴
  5. 예외가 발생했든 안했든 AUTOCOMMIT은 True로 바꿔줘야 한다(finally문 처리). 왜냐하면 커넥션은 한번 쓰고 버리는게 아니라 재사용(커넥션 풀에 다시 넣음)하기 때문이다.

Spring에서 트랜잭션

@Transactional을 이용해 비즈니스 로직이 아닌 반복적인 코드는 숨길 수 있다.

이때 디자인 패턴은 프록시 패턴, 템플릿 패턴을 사용하여 숨긴다.

즉, 위에서 이체와 관련된 부분만 코드에 나타낼 수 있다.

ACID란?

트랜잭션이 어떠한 속성을 가져야하는지 나타내는 개념이다.

Atomicity(원자성)

All or Nothing: 행하는 작업은 모두 성공하거나 모두 실패해야 한다.

Transaction의 정의는 논리적으로 쪼개질 수 없는 작업단위이기 때문에 내부의 SQL문들이 모두 성공해야 한다. 중간에 SQL문이 실패하면 지금까지의 작업을 모두 취소해야한다.

위의 계좌이체의 예시를 생각하면 된다.

Atomicity에서 DBMS가 담당하는 부분

  • DB에 데이터를 영구적으로 저장하는 것
  • Rollback 실행시 이전 상태로 되돌리는 것

개발자가 담당하는 부분

  • 언제 commit하거나 rollback할지를 정하는 것

Consistency(일관성)

예를 들어 100만원을 이체하려고 하는데 계좌에 80만원이 있다.

만일 100만원 이체를 성공했다고 한다면 계좌는 -20만원이 된다.

하지만 계좌는 음수가 될 수 없다는 제약조건이 걸려있으므로 이체가 실패한다.

즉, 이렇게 제약조건, trigger를 통해 DB의 규칙(rule)을 깨뜨리는지 확인하고 상태를 일관성있게 유지하는 것이 일관성이다.

Consistency에서 DBMS가 담당하는 부분

  • Transaction은 DB의 상태를 consistent 상태에서 또 다른 consistent 상태로 바꿔줘야 한다.
  • constraints, trigger 등을 통해 정의된 rule을 transaction이 위반했다면 rollback해야 한다.
  • transaction이 DB에 정의된 rule을 위반했는지 DBMS가 commit 전에 확인하고 알려준다.

개발자가 담당하는 부분

  • Application 관점에서 transaction이 consistent하게 동작하는지는 개발자가 확인해야 한다.

Isolation(격리성, 분리성)

만일 J가 H에게 20만원을 입금할 때 H도 본인 계좌에 30만원을 입금하는 경우

J가 H에게 20만원을 입금할 때 H가 가진 200만원을 읽어오고 거기에 20만원을 더하게 된다.

동시에 H도 본인 계좌의 200만원을 읽어오고 거기에 30만원을 더하게 된다.

원래 정상적인 결과는 250만원이 되어야하지만, 트랜잭션 사이에 트랜잭션이 끼어들어 최종적으로 220만원이 되어버렸다. 즉, 돈이 사라져 버린 것이다.

즉, 이런 문제가 발생하지 않으려면 트랜잭션이 격리된 상태여야 한다.

트랜잭션이 실행중일 때, 다른 트랜잭션이 끼어들면 안된다는 속성이 Isolation이다.

여러 Transaction들이 동시에 실행될 때도 혼자 실행되는 것처럼 동작하게 만드는 것이 Isolation의 목적이다. 하지만 isolation을 너무 엄격하게 하면 DB의 성능이 낮아지기 때문에 여러 종류의 Isolation level을 제공한다. isolation level이 높으면 높을 수록 다른 트랜잭션으로부터 영향을 받는 경우가 줄어들지만 트랜잭션을 동시에 실행시킬 수 있는 동시성이 떨어지게 되어 성능이 낮아지게 된다.

개발자는 DBMS가 제공하는 isolation level을 서비스에 맞게 적절하게 튜닝해야 한다.

concurrency control의 주된 목적이 isolation을 위한 것이다.

Durability(영존성)

commit된 transaction은 DB에 영구적으로 저장된다.

즉, DB 시스템에 문제(컴퓨터가 갑자기 종료됨, DB에 crash가 나서 DB프로세스가 갑자기 종료됨)가 발생해도 commit된 transaction은 DB에 남아있어야 한다.

그래서 DB 시스템을 다시 복구하게 되면 commit된 데이터는 DB에 남아있어야 한다.

→ 비휘발성 메모리에 저장되어야 하는 것을 의미한다.

→ Durability는 DBMS가 보장해준다.(물론 개발자가 튜닝할 수도 있다.)

profile
Get hands on dirty!🤺

0개의 댓글