https://dev2-jay.tistory.com/33
2차 프로젝트를 하면서 배우게 된 transaction과 동시성 제어의 기술에 대해 공부한 내용을 정리해 보겠다.
동시성 제어라는 것은 쉽게 설명하자면 말 그대로 어떤 현상이 동시에 일어날 때 어떤 식으로 처리해 줄 것인지 동시성을 제어하는 것을 의미한다.
문제점부터 파악해 보자!
사용자가 어떤 행위를 하여서 클라이언트 측에서 서로 다른 요청을 동시에 서버로 보내왔고, 요청에 따라 서버는 DB를 조작할 것이다. 이때 어떤 문제가 생기는 것일까? 주로 금융, 뱅킹서비스를 가지고 설명하니 나도 가장 대표적인 예제로 설명해 보겠다.
위와 같이 A와 B라는 사람이 있고, A가 B에게 2만 원을 송금한다고 생각해 보자. 첫 번째로 A의 계좌에서 2만 원이 차감되고, 두번째로 B의 계좌에 2만원이 더해질것이다. 서버에서는 이 과정을 두가지 다른 쿼리로 작성하는데 만약, 첫번째 쿼리문은 실패하고, 두번째는 성공한다면??? 갑자기 세상에 없던 2만원이 B라는 유저에게 생기는 것이다... 이러한 문제점을 방지하기 위해 Transaction 개념이 필요하다.
DB에서 Transaction이란
독립적인 단일 논리 작업단위를 뜻한다. ---> 논리적으로 생각했을 때 단일작업으로 한 번에 실행되어야 하는 것을 묶는 것
만약 transaction의 SQL문들 중 하나라도 실패하면 그 transaction은 DB에 반영되지 않는다.
이와 같이 transaction을 이용한다면, 위와 같은 문제가 발생했을 시 B가 받은 2만 원의 쿼리가 성공하였어도, DB에 결과를 반영하지 않기 때문에, 문제가 발생하지 않는다.
Transaction의 가장 중요한 개념 ACID:
Atomicity : 원자성 ( All or Nothing )
더 이상 쪼개질 수 없는 단위로 만드는 것. 만약 중간에 오류가 발생하면 rollback 시켜야 함.
Consistency: DB의 일관성을 지키자.
만약 위와 같은 그림 같은 예시에서, total amount >= 0과 같은 조건을 지정했을 시, A가 20만 원을 송금하는 명령을 한다면 A의 계좌는 -10만 원이 될 것이다. 이것은 Consistency 즉 DB의 일관성을 벗어나기 때문에 에러가 날것이다.
Isolation : 트랜잭션의 격리도를 정하자.
만약 아래와 같은 그림 예시에서, A가 2만 원을 B에게 이체하고(빨간색), 동시에 B도 자신의 계좌에 3만 원을 입금한다면?(파란색) 3만 원이 증발한다...
이렇게 여러 transaction들이 동시에 실행될 때, 하나의 transaction이 실행되는 것처럼 만드는 것이 Isolation이다.
DBMS는 개발자가 isolation level을 지정할 수 있도록 제공하고 있고, 개발자는 어떤 level로 transaction을 구성할 것인지 잘 판단하여 설계하여야 한다. 동시성 제어와 밀접한 속성이다.
Durability : commit 된 transaction은 영구적으로 DB에 보존되어야 한다.
만약 DB에 문제가 생겨도 commit 된 transaction의 기록들은 영구적으로 남아있게 된다.
Transaction의 Schedule과 Serializability:
Serial schedule : transaction들이 겹치지 않고 한 번에 하나씩 실행되는 schedule
Non-serial schedule : transaction들이 겹쳐서 동시에 실행되는 schedule
두 개의 schedule을 비교해 보면 당연히 동시에 실행되는 nonserial schedule이 성능이 더 좋다. 하지만, nonserial schedule을 사용하면 위의 isolation의 예시와 같이 예상하지 않은 결과가 나올 확률이 매우 높아지는데 이것을 해결하기 위해서 DB에서의 Conflict을 알아야 한다.
아래의 세 가지 조건을 만족하면 Conflict 하다라고 할 수 있다.
1. 서로 다른 transaction
2. 같은 데이터에 접근
3. 최소 하나는 write 오퍼레이션
위의 예시에서는 C schedule이 conflict 하다고 할 수 있다.
여기서 만약 두 operation의 순서가 바뀐다면, 결과도 바뀌게 된다. 이 부분에서 Conflict equivalent를 정의해야 한다.
그다음 Conflict equivalent를 알아야 하는데 아래와 같다.
1. 두 Schedule은 같은 transaction들을 가진다.
2. 어떤 conflict operations의 순서도 양쪽 schedule 모두 동일하다.
가장 중요한 Conflict serializable은 serial schedlue과 conflict equivalent일 때, Conflict serializable라고 한다.
따라서, schedule C는 Conflict serializable 하다.
여기서 결론은 conflict serializable 한 nonserial schedule을 허용한다면 성능과 결과를 동시에 챙길 수 있다는 것이다.
하지만, 현실에서는 이런 식으로 모든 transaction을 검사하면서 conflict serializable 한지 확인하는것도 성능을 저하시키기 때문에, 아예 conflict serializable한 schedule을 보장하도록 만든 프로토콜을 따로 적용시키는 방법으로 구현한다.
Isolation level:
- Dirty read
- Non-repeatable read
- Phantom read
위의 현상들은 transaction의 이상현상들인데, 세 가지 현상을 모두 다 발생시키지 않기 위해서는 isolation level이 너무 높아져 결과적으로 db의 성능저하를 일으킨다. 따라서 DBMS는 개발자에게 어느 정도 허용할 것인지에 대한 선택권을 준다.
Isolation level | Dirty read | Non-repatable read | Phantom read |
Read uncommitted | O | O | O |
Read committed | X | O | O |
Reapeatble read | X | X | O |
Serializable | X | X | X |
이밖에도 dirty write, lost update, read skew, write skew 등등 transaction 처리에서 일어날 수 있는 오류들이 있는데 필요할 때 (오류를 맞이했을 때...) 좀 더 자세히 찾아보자.
마무리 :
지금까지 공부한 내용은 동시성 제어를 위한 기본 배경지식이었다. 다음 글에서 실질적으로 위의 내용을 이용해서 어떤식으로 동시성 제어 기술을 사용하는지 알아보자.