트랜잭션

jinwook han·2021년 11월 28일
0

데이터 시스템

목록 보기
2/7
post-thumbnail

데이터 중심 어플리케이션 설계 7장을 정리했습니다.
책을 기반으로 글을 썼지만, 임의의 추가 정보나 저의 주관적인 해석이 추가될 수 있습니다.
느낀 점은 책 저자의 견해가 아니라 저의 견해입니다.

서론

데이터 중심 어플리케이션 디자인 7장은 트랜잭션에 대해 다룬다.
이 장에서는 트랜잭션의 의미, ACID 원칙, 트랜잭션을 적용 시 생길 수 있는 문제점, 그에 대한 해결 방법 등을 다룬다.
먼저 책 내용을 정리한 뒤, 느낀 점을 적는다.

책 내용

트랜잭션 정의

트랜잭션은, 읽기 및 쓰기 여러 개를 하나로 묶을 수 있는 단위 개념이다.
트랜잭션을 적용하면 부분적 성공, 부분적 실패라는 개념을 없앨 수 있다.
트랜잭션 내에서는 온전한 성공 또는 온전한 실패만이 존재할 수 있다.

트랜잭션은 프로그래밍을 단순히 하고자 하는 목적에서 나온 개념이다.
트랜잭션이 없으면 부분적 실패에 대한 예외 로직을 추가해야 한다.

ACID

ACID 원칙은 트랜잭션이 무엇을 보장하는가를 설명한다.

  • Atomicity(원자성) :
    한 트랜잭션 내에서는 부분적 실패 및 부분적 성공이 없어야 함을 의미한다.
    부분적으로 실패하면 전체적인 실패로 간주하여 롤백한다.
    Atomicity보다는 Abortability(포기성)라는 단어가 원칙을 잘 설명한다고도 볼 수 있다.

  • Consistency(일관성) :
    트랜잭션 이후에도 이전과 같이 특정 논리가 지켜져야 함을 의미한다.
    계좌이체를 하는 경우, 돈이 빠져나간 계좌가 200만원이 빠졌다면, 돈이 들어간 계좌는 정확히 200만원이 더 들어가야 할 것이다.
    '계좌이체에 참여한 두 계좌의 총합은 트랜잭션 이전/이후 모두 같아야 한다.'는 원칙이 있는 것이다.
    하지만 위 원칙은 송신자의 계좌 정보가 없는 거래 데이터를 insert만해도 원칙은 깨진다.
    Consistency는 트랜잭션만으로 보장되는 것이 아니기 때문에, ACID 의 다른 원칙들과 성질이 같다고 보기는 힘들다.

  • Isolation(격리성) :
    서로 존재를 모르고 시작한 두 트랜잭션은, 서로 영향 받는 일이 없어야 한다는 원칙이다.
    실제로 완벽하게 서로 영향받지 않게 트랜잭션을 적용하는 일은 드물다.
    실패하는 트랜잭션이 너무 많아질 수 있기 때문이다.
    여러 데이터베이스에서는 낮은 수준의 격리성을 제공한다.

  • Durability(내구도) :
    트랜잭션 이후 커밋된 데이터는 유실되지 않아야 함을 의미한다.

오늘날 데이터베이스마다 ACID 구현은 모두 다르다.

트랜잭션에서 발생할 수 있는 문제점

트랜잭션 여러 개가 서로 인지하지 못하고 독립적으로 이뤄질 때 dirty read, dirty write, lost update, write skew와 같은 여러 가지 문제점들이 발생할 수 있다.
각각의 문제점들이 어떤 문제인지 정리한다.

dirty read

한 트랜잭션이 다른 트랜잭션의 미완료된 변경사항을 읽을 수 있는 상황을 의미한다.
예로 들어 두 개의 테이블을 업데이트하는 하나의 트랜잭션이 끝나지 않은 시각에서, 다른 트랜잭션은 하나의 테이블 업데이트 반영분만 읽는 상황이다.
두 테이블 간에 논리적 관계가 있는 경우, 사용자에게 혼란을 줄 수 있어 문제가 된다.

read skew

한 트랜잭션이 다른 트랜잭션의 완료된 변경사항을 읽을 수 있는 상황이다.
모든 상황에서 문제가 되는 것은 아니며, 특정 상황에서 문제가 된다.
예로 들어 디비 백업을 하는 동시에 트랜잭션 A를 반영한다고 할 때, 특정 row는 트랜잭션 A가 적용되기 이전의 버전으로, 특정 row는 트랜잭션 A가 적용되기 이후의 버전으로 백업이 될 수 있어, 데이터 정합성에 무제가 생길 수 있다.

dirty write:

트랜잭션들의 커밋 순서가 트랜잭션들의 시작 순서와 일치하지 않으면서, 모든 트랜잭션이 성공하는 상황을 의미한다.
상품이 하나밖에 없는 상황에서, 구매자가 두명이 동시에 구매 트랜잭션에 진입했을 경우, 이 두 구매자들의 트랜잭션들이 모두 성공하면 데이터 정합성에 문제가 생길 수 있다.

lost update:

트랜잭션들의 커밋 순서는 트랜잭션들의 시작 순서와 일치하지만, 나중에 들어온 트랜잭션의 커밋이 이전 트랜잭션의 커밋 내용을 덮어쓰는 상황이다.
같은 상품 데이터를 수정하는 작업자 두 명이 동시에 수정 과정에 진입했을 때, 한 작업자 A가 id, 수량을 수정하고, 다른 한 작업자 B가 가격을 수정하여 B가 먼저 커밋했을 때, A의 수정사항(id, 가격)이 반영되지 않는다는 문제점이 있다.

write skew:

트랜잭션을 시작할 때의 데이터 내용과, 트랜잭션이 끝나기 직전(트랜잭션 내용이 반영되기 전)의 데이터 내용이 다른 상황이다.
lost update, dirty write를 모두 포함하는 개념이다.

예로 들어, 방을 예약하는 과정에서 write skew 나타난다.
방 예약 과정에서, 특정 시간 특정 방을 사용할 수 있는 사람은 1명 뿐이다.
방 예약하려는 사람 두 명이 동시에 예약 과정에 진입하고, 두 사람 모두 같은 시간, 같은 방(비어 있는 방)을 예약했다.
이 경우, 특정 시간 특정 방을 사용할 수 있는 사람은 1명 뿐이라는 원칙이 깨질 수 있으므로 문제가 된다.

isolation 방식에 따라 위 문제들 일부 또는 전부를 해결할 수 있다.

isolation

isolation은 진행 시간대를 공유하는 트랜잭션들을 이슈없이 처리하기 위한 전략이다.
isolation 종류에 따라 트랜잭션 동시성 이슈 일부 또는 전체를 예방할 수 있다.
islation 종류로 read committed, snapshot islation, two phase locking, serializable snapshot isolation을 정리한다.

read committed

read committed은 dirty read, dirty write를 방지하는 격리 수준이다.
트랜잭션 커밋이 반영되는 즉시 동시에 진행되는 다른 트랜잭션에도 완료된 커밋을 반영하는 방법으로 dirty read를 해결한다.
그리고 쓰기 트랜잭션을 진행하는 data들에 lock을 거는 방식으로 dirty write를 방지한다.

snapshot isolation

snapshot isolation은 read skew를 방지한다. 트랜잭션이 시작되면, 트랜잭션이 끝날 때까지 트랜잭션이 시작했을 때의 버전으로 데이터를 읽도록 강제한다.
트랜잭션이 시작된 시점의 데이터를 따로 저장하고 있어야 한다. 따라서 하나의 데이터의 과거 이력들을 저장하는 전략도 필요하다.

two phase locking

two phase locking은 위에 나온 모든 이슈들을 방지한다.(dirty read, dirty write, lost update, write skew)
two phase locking에서는, 먼저 들어온 write 트랜잭션이 후에 들어온 read 트랜잭션을 대기하게 만들 수 있고, 먼저 들어온 read 트랜잭션이 나중에 들어온 write 트랜잭션을 대기하게 만들 수 있다.

serializable snapshot isolation

serializable snapshot isolation 또한 위에 나온 모든 이슈들을 방지한다.(dirty read, dirty write, lost update, write skew)
serializable snapshot isolation은 긍정적 락 기법을 사용한다. 각 트랜잭션이 커밋되기 전까지는 트랜잭션을 abort하지 않는다.
트랜잭션이 커밋되는 순간, write skew가 일어날 수 있는 트랜잭션을 감지하여 롤백 여부를 판단한다.

위와 같은 isolation 기법들을 선택적으로 적용하여, 트랜잭션으로 발생하는 동시성 이슈들을 해결할 수 있다.

느낀 점

트랜잭션은 실무에서 많이 쓴다. 하지만 위와 같은 개념들이 크게 와닿지는 않았다. 격리수준을 생각하며 트랜잭션을 사용할 일이 적었기 때문이다.

회사에서는 다음과 같이 해결한 적이 많았다.
dirty read는 jpa로 해결한다.(1차 캐시로 repeatable read를 보장한다)
read skew는 해결하지 않는다. dirty write, write skew는 mysql user lock으로 해결한다.

mysql user lock으로 코드 블럭에 대한 lock을 걸 수 있다. 따라서 방을 예약하는 코드가 있다고 할 때, 방을 예약하는 코드에 대한 락을 거는 게 가능하다.
lock에 이름을 줄 수 있으므로, 특정 방 id prefix를 붙이기만 한다면 특정 방에 예약 과정에 진입하려는 트랜잭션을 하나로 제한할 수 있다.

앞으로도 lost update, write skew와 같은 문제가 발생한다면, mysql user lock에 관한 코드를 추가하는 방식으로 해결할 것 같다.

0개의 댓글