Transaction과 ACID

모두·2025년 3월 12일

오늘은 DB에 대해서 공부할 때 알아두면 좋은 데이터베이스 트랜잭션과 일관성 유지 기법 ACID 에 대해서 글을 작성하려 한다.

트랜잭션(Transaction)

  • 트랜잭션은 논리적 작업의 단위로, 하나의 작업 단위를 의미

  • 모든 작업이 성공적으로 완료되거나, 전부 취소(롤백)되는 것을 보장

  • 트랜잭션은 데이터의 일관성무결성을 유지하기 위해 중요

  • 예시

    • 은행 송금: 두 작업이 하나의 트랜잭션으로 처리
      • 계좌 A에서 돈을 인출
      • 계좌 B에 돈을 입금
    • 랜(선)뽑(기): 패배 기록이 안남게 경기에서 질거 같으면 인터넷을 끊음

보통 MySQL 을 사용하는 이유가 InnoDB 가 Transaction을 거의 완벽하게 지원해준다.

트랜잭션의 특징 = ACID

  • Atomicity(원자성)
    • 트랜잭션의 모든 작업이 전부 수행되거나 전혀 수행되지 않음을 보장
    • 실패 시, 모든 변경 사항이 Rollback
  • Consistency(일관성)
    • 트랜잭션 실행 전과 실행 후 DB가 일관된 상태를 유지함을 보장
  • Isolation(격리성)
    • 트랜잭션이 동시에 실행될 때, 각각의 트랜잭션이 독립적으로 실행되도록 보장
    • 트랜잭션간 간섭 X
      • 첫 번째 시도한 송금이 실패했다고 두번 째 송금에 영향을 주면 안된다.
  • Durability(지속성)
    • 트랜잭션이 성공적으로 완료되면, 그 결과는 영구적으로 저장
    • 시스템 장애가 발생하더라도 데이터는 손실 X

여기서 session 의 개념이 적용된다

  • connection 하나를 가져와서 connection에서 session을 생성하고 그 session안에 쿼리를 잔뜩 쌓아놓은 다음에 이 session을 한번에 적용한다.

accounts 라는 테이블 만들고 데이터 넣어놓았다.

현재 1번계좌에 돈이 있고 2번에 없기 때문에 1번이 2번으로 송금을 해야 한다.

쿼리문 한줄 씩 먼저 transaction을 돌리고 100 원을 뺴준 뒤 데이터 확인 해보면

100 빠졌다.

만약 이상태에서 ROllBACK 을 하면 어떻게 될까?

  • ROllBACK을 하고 다시 데이터 조회해보면

100 을 뺏던게 원상태로 돌아오면서 ACID 에서 원자성과 일관성을 잘 된걸 확인할 수 있다.

이번에는 1에서 100을 빼고 2에 100을 넣어주는 쿼리를 한번에 실행한 후 데이터 조회해보면

2에다가 100 이 송금됨

이후 COMMIT 을 하게 되면 최종적으로 적용이 되는 것이고

COMMIT 을 통해 적용 후 ROLLBACK 을 하여도 이미 DB에 반영되었기에 다시 ROLLBACK 되지 않는다.

이후 INNODB 에서 상태변화를 체크해 보기 위해서 쿼리문 다시 실행하고

SHOW ENGINE INNODB STATUS; 쿼리 통해서 값 체크해보면 아래처럼 나오는데

  • 현재 위에서 1에 100을 빼는 쿼리만 실행하고 아직 commit을 하지 않았기 때문에 젤 하단에 값 보면 lock 걸려 있다는 내용을 볼 수 있음(아직 트랜잭션이 묶여있다.)

이후 COMMIT 을 하고 다시 SHOW ENGINE INNODB STATUS 을 해보면

  • 아까 존재하던 lock 이 사라짐

정리 : INNODB 를 통해 현재 트랜잭션이 얼마나 쌓였나 관찰 가능

트랜잭션의 상태

트랜잭션이 현재 어떤 상태인지 아는 것도 중요하다.

Transaction과 ACID

  • Active: 트랜잭션이 실행 중이며, 아직 완료되지 않은 상태.
  • Partially Committed: 트랜잭션의 마지막 연산이 실행된 후, 모든 변경 사항이 커밋되기 전 상태.
  • Committed: 트랜잭션이 성공적으로 완료되어 모든 변경 사항이 데이터베이스에 저장된 상태.
  • Failed: 트랜잭션 중 오류가 발생하여 더 이상 실행을 진행할 수 없는 상태.
  • Aborted: 트랜잭션이 실패하여 모든 변경 사항이 롤백된 상태.

처음에 start transaction 이 되면 active 상태가 된다 (쿼리 언제들어 오나 살펴보고 있는 상태)

  • 위에서 InnoDB를 통해 봤던 active 된 transaction

Partially Committed 에서 쿼리가 하나씩 처리됨

그러다 Failed 상태가 되면 ROllBACK 을 통해 Aborted 상태가 된다.

반면에 모든 쿼리가 성공하면 Committed 상태로 완료됨

정리 : 트랜잭션은 크게 2가지의 위의 흐름으로 진행된다.

트랜잭션 관리

  • 로그: 트랜잭션의 변경 사항을 기록해서 장애시 복구

    • 로그만 남기는 것 보다는 어디서 끊겼는지를 알아야하기에 체크포인트 중요하다
  • 잠금(Lock) 매커니즘: 동시성 제어로 트랜잭션 간 간섭을 방지

    • 실패한다하면 트랜잭션을 끊고 ROLLBACK 한다음에 다시 나중에 시도해야 한다.
  • 체크포인트: 현재 시스템 상태를 저장해서 장애 발생시 체크포인트부터 복구할 수 있도록 구성

    • 트랜잭션 중간 중간에 이 트랜잭션 상태를 어디다가 저장을 할 수 있는 곳을 만들어 두어야 한다.

트랜잭션 관리를 위한 격리 수준

  • Read Uncommitted
    • 트랜잭션이 커밋되지 않은 데이터를 읽을 수 있음. 데이터 무결성이 낮음
      • 위의 예시 실습에서 accounts 계좌에 1에서 100 뺴주는 쿼리 실행 후 바로 계좌 확인 가능했다.
      • COMMIT 되지 않은 데이터는 언제든지 ROLLBACK 이 가능하기 때문에 대도록이면 Uncommitted 상태는 읽으면 안된다.
  • Read Committed
    • 트랜잭션이 커밋된 데이터만 읽을 수 있음. 다른 트랜잭션의 중간 변경 사항을 읽을 수 없음
  • Repeatable Read
    • 트랜잭션이 시작된 시점의 데이터를 읽으며
    • 동일한 쿼리를 여러 번 실행해도 동일한 결과를 반환
      • start transaction 이 시작되면 그 상태에서 memory 에 띄워놓고 유지하고 있는 것
      • 이렇게 되면 COMMIT 되기를 기다릴 필요없다. COMMIT 되기 전 데이터를 보고 있기 때문
  • Serializable(가장 높은 격리 수준)
    • 트랜잭션이 순차적으로 실행되는 것처럼 동작
    • 동시성 제어를 엄격하게 하여 데이터 무결성을 보장

보통 MySQL 에서 Read Committed 만 사용해도 충분함

  • 그 이상을 원한다면 MySQL 에서 transaction lock 을 쓸게 아니라 memory 상에서 병렬 처리나 동시성 처리를 할 수 있게 따로 사용하는 것이 좋다. (요즘 GO 많이 사용함)

Serializable 은 트랜잭션에 또 트랜잭션을 거는 수준이기에 ROLLBACK 이 잦다.

  • 하위 트랜잭션이 ROLLBACK 나면 당연히 상위도 ROLLBACK 되기 떄문
  • 무결성은 보장하지만 많은 retry가 필요할 수 있다.

0개의 댓글