DataBase Transaction

hyeok3011·2023년 11월 24일
0

이번에는 Database의 Transaction에 대해서 다루어 보려고 한다.

Transaction

Transaction은 데이터 베이스의 상태를 변경시키는 여러 읽기/쓰기 등 작업을 하나의 논리적인 작업으로 묶음을 뜻한다.

상태를 변경 시킨다는 뜻은 우리가 SQL을 사용하여 Database에 어떠한 명령을 내린다는 뜻이다.

그렇다면 여러 작업을 하나의 논리적인 작업으로 묶는다는게 무슨소리일까?

유명한 예시를 보자

우리는 은행 시스템을 만들고 있고 A라는 사람이 B라는 사람에게 돈을 송금하는 작업을 개발해야한다.
위 기능에 총 데이터베이스 작업은 3가지 이다.
1. A사람의 돈이 송금할 금액만큼 있는지 확인한다.
2. A사람의 송금할 돈을 차감한다.
3. B사람의 기존 돈에 송금된 돈을 더한다.

만약 1,2번 작업을 수행한 뒤 이유모를 원인으로 3번 작업 진행중 실패한다면 어떻게 해야할까?
이러한 문제가 생기면 개발자가 직접 A사람의 돈을 복구시키는 보상조치를 해줘야 할것이다.

만약 위 모든 작업을 하나의 작업으로 묶어 3번 작업을 실패하면 알아서 2번 작업이 취소되면 얼마나 좋을까???

Atomicity(원자성)

Transaction은 1,2,3번 작업을 하나의 논리적 작업으로 묶어 3번에서 실패하는 경우 2번까지 같이 복구해준다. 이를 Transaction의 Atomic(원자성)이라고 부른다.

  • 여러 읽기/쓰기 작업을 하나의 논리적인 작업으로 묶어 모두 성공한다면 Commit하여 모두 저장하고 하나라도 실패하면 모두 Rollback시킨다.
  • 원자성은 "all-or-nothing"의 원칙을 따른다.


Consistency(일관성)

트랜잭션의 consistency는 트랜잭션이 완료된 결괏값이 일관적인 DB 상태를 유지하는 것을 말한다.
일관된 상태의 데이터베이스에서 하나의 트랜잭션을 성공적으로 완료하고 나면 그 데이터베이스는 여전히 일관된 상태여야 한다. 즉, 트랜잭션 수행 전과 후에 데이터 모델의 모든 제약 조건(기본 키, 외래 키, 도메인, 도메인 제약조건 등)을 만족하는 것을 통해 보장한다.


이번에는 다른 상황의 예시를 들어보겠다.

하루에 송금이 가능한 횟수는 3번이다.
만약 이미 A유저는 2번을 송금한 상태에서 동시에 2개의 송금 트랜잭션이 시작되면 어떻게 될까????

우리는 이와 같이 여러 작업이 같은 데이터에 접근하려는 상황에서 생기는 문제를 Race Condition(경쟁 상태)라고 한다.

Isolation(격리성)

데이터 베이스는 이러한 경쟁상태를 대비하기 위해 실행 중인 트랜잭션의 중간결과를 다른 트랜잭션이 접근할 수 없도록 막는다. 이를 Transaction의 Isolation(격리성) 이라고 부른다.

먼저 격리성을 지원하지 않을때 생길 수 있는 문제들에 대해서 알아보겠다.

  • Dirty Read
    Dirty Read는 다른 트랜잭션에 의해 수정됐지만 아직 COMMIT되지 않은 데이터를 읽는 것을 말한다. 아직 확정되지 않은 데이터를 기반으로 연산을 수행을 수행했을 했는데 원본 트랜잭션이 ROLLBACK될 경우 잘못된 결과가 나올 수 있다. (비슷한 예로 lost updates가 생길수도 있다.)
  • Non-Repeatable Read
    한 트랜잭션이 동일한 데이터를 두 번 읽을 때, 두 번째 읽기에서 다른 결과를 얻는 경우이다. 이는 중간에 다른 트랜잭션이 해당 데이터를 변경하거나 업데이트할 때 발생한다.
  • Phantom Read
    한 트랜잭션이 동일한 쿼리를 두 번 실행했을 때, 첫 번째 쿼리와 두 번째 쿼리 사이에 다른 트랜잭션이 새로운 데이터를 삽입or삭제한 경우에 발생된다.

이런 문제들을 방지하고자 Transaction의 4가지 격리 수준을 지원한다.
격리 수준에 대한 내용은 아래에서 다루겠다.


Durability(영속성)

트랜잭션의 Durability(영속성)은 커밋이 완료된 트랜잭션의 내용은 영구적으로 유지되도록 보장하는 속성이다.
시스템의 오류, 충돌, 전원 손실과 같은 예상치 못한 상황이 발생해도 완료된 트랜잭션의 결과가 보존되도록 보장된다.

영속성 메커니즘

그러면 어떻게 영속성을 보장할까??
모두 이해하지 못했습니다. ㅠㅠ.... 글 마지막 링크를 봐주세요 ㅠ


Transaction의 격리 수준

Transaction에는 4가지 Isolation Level(격리 수준)이 존재한다.

Read Uncommitted

이 수준에서는 한 트랜잭션이 커밋되지 않은 변경 사항을 다른 트랜잭션이 볼 수 있다. "Dirty Read" 현상을 허용하는데, 이는 한 트랜잭션이 아직 완료되지 않은 다른 트랜잭션의 변경사항을 읽을 수 있음을 의미한다. 데이터 일관성에 문제가 있어 실제로 사용하는지는 잘 모르겠다.

Read Committed

Commit된 데이터만 읽을수 있고 Commit되지 않은 데이터는 읽지 않는다.
하나의 트랜잭션이 데이터를 수정한 상태로 작업 진행중인 상황에서 다른 트랜잭션이 읽으려고 접근한다면 수정되지 않은 기존의 데이터만 읽을 수 있다.
Write는 Lock이 걸려있는 상태이다.

이 수준의 격리레벨을 사용한다면 "Dirty Read"의 문제는 발생하지 않지만 "Non-Repeatable Read" 현상은 발생될 수 있다.

Repeatable Read

트랜잭션이 진행되는동안 데이터가 변경되더라도 원본 데이터를 읽게 만든다.
즉 읽는 시점에서의 특정 버전에 해당하는 데이터만 읽는다고 생각하면 될듯 하다.

이 수준의 격리 레벨에서는 Non-Reapeatable Read는 발생하지 않지만 범위 잠금은 관리되지 않으므로 "Phantom Read" 발생할 수 있다.

그리고 Repeatable Read수준에서는 Write skew, lost update가 발생될 수 있다.
(lost update같은 경우에는 atomic연산, 명시적잠금 방법으로 해결할 수 있다.)

Serializable

이 수준은 가장 높은 격리 수준으로, 트랜잭션이 다른 트랜잭션의 결과에 전혀 영향을 받지 않도록 보장한다.

모든 트랜잭션을 줄세워 할거같지만 실제로는 그렇지는 않는다.
시스템마다 조금씩 다르겠지만 Where문을 확인하여 range locking 또는 row locking 같은 강력한 locking전략을 사용한다고 한다.

직렬화 가능 수준에서는 트랜잭션이 완전히 독립적으로 실행되며, 이는 데이터의 일관성과 무결성을 최대화하지만 동시성이 크게 감소하여 성능에 부정적인 영향을 줄 수 있다.


위에서 소개한 Transaction 4가지 특성을 우리는 ACID라고 한다.
위와 같은 트랜잭션의 속성이 없다면 개발자가 직접 어플리케이션 단에서 관리 해줘야 한다. (실제로 그래야하는 일이 있음ㅠ....)
Database를 사용하는 개발자라면 Transaction이 뭔지 내가 사용하는 DBMS의 기본 isolation lelvel은 뭔지 반드시 알아야 한다고 개인적으로 생각한다.
내 비즈니스 코드는 모두 완벽한거 같은데 왜 DB에 이상한 일이 생기는거지??? 하면 범인은 바로 Transaction일 것이다.




마치며

Transaction에 대해서 정리해 봤다. 아직 부족한 내용들이 있다.
Transaction을 정리하고 Distributed Transaction을 정리할 생각이였지만 시간이 걸릴듯 하다.
정리하면서 느끼는것이 나는 알고있다고 착각하고 있었다는 것이다.
머릿속의 파편된 내용들을 글로 정리해도 이렇게 어렵고 모르는것이 많은데
내가 제대로 알고 있는게 있나 싶기도 하다.


참고한 내용

다루지 못한 내용(꼭 읽어야함...)

profile
뇌가 디스크가 아니라는 사실을 깨달아 버린 사람

0개의 댓글