땃쥐네

[DataBase] 트랜잭션 및 트랜잭션의 4대 특성(ACID) 본문

DataBase

[DataBase] 트랜잭션 및 트랜잭션의 4대 특성(ACID)

ttasjwi 2022. 7. 31. 16:50

마일리지 거래 사례를 통해 확인하는 '트랜잭션'의 필요성

어떤 서비스에서 구매자 '철수'와 판매자 '영희'가 어떤 제품을 거래하는 상황을 상상해봅시다.

 

대표적으로 아xx매니아와 같은 온라인 게임머니 거래 사이트)가 있겠네요. 여기서는 일단 게임머니 거래, 수수료와 같은 복잡한 상황을 고려하지 않고 단순히 마일리지를 주고 받는 상황을 고려해보겠습니다.

`

철수가 영희에게 1만 마일리지를 지불하는 서비스 로직을 좀 더 작은 단위로 분리해보면

 

1. 철수의 마일리지에서 1만 마일리지를 차감

2. 영희의 마일리지에 1만 마일리지를 적립

 

이 두 가지가 모두 이루어져야합니다.

뭐긴 뭐야 서비스 종료지

그런데, 1번은 성공했는데, 2번 작업 진행 과정에서 시스템에 심각한 오류가 발생해서 작업이 진행되지 않는다면 무슨 일이 벌어질까요? 철수의 마일리지 잔고만 1만 감소한 결과를 낳는 이상한 상황이 터져버리는 겁니다. 이런 일이 한번, 두번 그 이상 터지면 운영진이 사과문 쓰는 것으로 끝나지 않겠죠. 그 서비스는 신뢰를 잃고 망해버릴겁니다.

 

마일리지 거래라는 상황은 논리적으로 하나의 작업으로 처리되어야하고, 완전히 성공하거나, 완전히 실패해야합니다. 내부적인 작은 로직들 하나하나가 부분적으로만 성공해버려서는 안 됩니다. 이를 보장하기 위해 도입해야하는 개념이 '트랜잭션'입니다.


트랜잭션(Transaction)

 

영어 단어 Transaction은 '거래'를 의미하는 영어단어인데요, 데이터베이스에서 트랜잭션은 하나의 거래, 더 일반화해서 '논리적인 작업'을 안전하게 처리하는 것을 보장해주기 위해 도입한 개념입니다.

 

'거래'라는 논리적인 작업은 작게 구매자의 마일리지 차감, 판매자의 증가 두 가지 작업이 묶여있지만 더 작은 단위로 쪼갰을 때는 '거래'의 의미를 잃게 됩니다. 더 쪼갤 수 없고 반드시 '하나의 단위'로 이루어져야하죠. 더 작업 단위를 쪼갰을 때 의미를 상실하게 되므로, 하나의 단위로 이루어져야 의미를 가지는 비즈니스 로직. 이를 '논리적인 하나의 작업'이라 할 수 있으며 이를 '트랜잭션'으로 칭할 수 있습니다.

update member as m
set m.money = m.money -10000
where m.member_id = 1;

update member as m
set m.money = m.money + 10000
where m.member_id = 2;

트랜잭션은 실제 구현된 결과물의 관점에서 놓고 보면 하나 이상의 데이터베이스 연산으로 구성되어 있습니다. SQL의 모음이라고 봐도 무방합니다. 비즈니스 로직의 복잡도에 따라, 단순히 하나의 연산으로만 구성될 수도 있고, 두개 이상으로 구성되어 있을 수도 있죠.

 

트랜잭션은 아래에서 후술하겠지만 성공 혹은 실패 두 가지 결과를 낳게 되고 그 결과가 '데이터베이스 로그'로 남게 되는데, 이 로그를 토대로 장애가 발생했을 때도 데이터 유출로부터 어느 정도 안전하게 회복할 수 있습니다. 이런 관점에서 트랜잭션은 장애 발생 시 데이터를 복구하는 작업의 단위로 볼 수도 있습니다.

 

결론

트랜잭션 = 논리적 작업 단위 = 하나의 비즈니스 로직 = 1개 이상의 SQL
= 장애발생 시 데이터 복구의 기본 단위

커밋(Commit)과 롤백(Rollback)

트랜잭션의 결과는 실제로 전부 성공(All Success)하거나 전부 실패해야합니다.(All Failure)

그리고 그 성공 실패에 따른 결과를 설명할 용어가 필요합니다.

 

트랜잭션 내의 모든 작업이 성공해서 데이터베이스에 정상 반영되는 것을 커밋(Commit)이라고 하고,

 

트랜잭션 내의 작업 중 하나라도 실패했을 경우 이전 상태로 되돌리는 것을 롤백(Rollback)이라 합니다.
하나라도 실패한 결과 롤백이 일어나는 덕분에 우리 데이터베이스는 데이터 무결성을 유지할 수 있습니다.


트랜잭션의 4대 특성 : ACID

트랜잭션은 데이터베이스의 무결성과 일관성을 보장하기 위해 ACID라 하는 4가지 특성을 보장해야합니다. (참고링크 : https://en.wikipedia.org/wiki/ACID)

 

 

 

1. 원자성(Atomicity)

모두 성공
모두 실패

트랜잭션 내에서 실행된 작업들이 마치 하나의 작업인 것처럼 모두 성공하거나, 모두 실패됨이 보장되어야함(All Or Nothing)을 나타내는 특성입니다. 도중에 실패되었을 때 롤백이 되는 현상이, 트랜잭션의 원자성을 나타냅니다. 

 

2. 일관성(Consistency)

일관성을 만족할 때
일관성 만족 x

트랜잭션에서 데이터 조작 전 후에 데이터베이스가 일관된 상태를 유지해야함을 나타내는 특성입니다.

예를 들면, 계좌이체 전후에는 구매자의 마일리지에서 빠져나간 금액만큼 판매자의 잔고에 딱 그만큼 입금되어야하는 것이 이에 해당합니다.

 

또, 트랜잭션이 시작되기 전이든 종료된 후이든 항상 데이터베이스에서 정의된 무결성 제약조건을 만족해야합니다.

예를 들어, 어떤 회원가입 로직이 수행된 뒤에 같은 로그인 아이디를 가진 회원이 둘 이상 있어서는 안 된다거나, 거래 작업 후에 어떤 회원의 마일리지가 음의 값(예 : -19900원) 을 가져서는 안 되는 것도 이에 해당합니다.

 

3. 격리성(Isolation)

동시에 실행되는 트랜잭션들이 서로에게 영향을 미치지 않도록 격리해야함을 나타내는 특성입니다.

 

단순히 한 사용자가 데이터베이스를 이용하는 상황을 고려하면 간단하겠지만, 실제로 데이터베이스는 여러 사용자가 거의 동시에 사용합니다. 이 부분이 트랜잭션의 개념을 학습하는 과정에서 제일 까다로운 부분입니다.

 

 

 

어떤 회원 테이블에, 철수의 국적가 'KOR'로 등록되어 있는 상황을 생각해봅시다.

 

A 트랜잭션에서는 철수의 국적을 'USA'로 변경시키는 로직이 수행됩니다.

B 트랜잭션에서는 테이블에 속한 한국인의 인구를 한 번 조회하고, 미국인의 인구를 한 번 조회하는 로직이 수행됩니다.

(적절한 예시를 들어야하는데, 마구잡이로 이상한 예시를 들었습니다. 죄송합니다 ㅜㅜ)

 

예를 들어, 트랜잭션 A 진행 도중 철수를 미국인으로 변경하고 커밋하지 않은 상태에서, 이 변경 결과가 트랜잭션 B에서 Select 문에 영향을 끼치게 되면 어떻게 될까요? 철수는 트랜잭션 B에서, 한국인으로 집계되지 않을 것입니다.

 

그런데 이 상태에서 롤백을 시키고 트랜잭션 B에서 select문을 또 돌려서, 미국인을 집계한다면 이 select문에서도 철수는 계산되지 않을 겁니다. 결과적으로 철수는 한국인으로도 미국인으로도 집계 되지 않은 상황이 되버립니다.(순서가 바뀌어 미국인으로 집계되더라도 그 자체를 놓고 보면 이상한 상황이였을거에요.)

 

단순하게 이런 상황이면 모르겠지만, 금전적인 문제가 엮인 문제라면 더 복잡해진 상황이 될겁니다. 예를 들어 호텔을 예약할 때 빈 객실이 있더라도 그게 조회가 안 된다거나, 이러면 경제적으로 손해를 발생시킬 수 있겠죠.

 

격리성을 완전히 보장하기 위해서는 트랜잭션을 순서대로 실행하면 됩니다.

 

A 트랜잭션이 일어나는 동안 B 트랜잭션에서는 같은 데이터에 접근할 수 없도록 하는게 가장 이상적이죠. 하지만 동시요청이 10만건, 100만건... 이렇게 늘어나면 이를 순서대로 수행해야할텐데 감당하기 매우 힘들어질겁니다.

그래서 ANSI 표준은 트랜잭션의 격리 수준을 4단계로 나누어 정의하였고 각 데이터베이스에서는 4단계 격리 수준을 선택적으로 사용할 수 있게 하였습니다. 트랜잭션을 순서대로 수행하지 않고 적절한 단계의 격리수준을 선택하여 동시처리를 유연하게 처리할 수 있게 되고(실제로는 Read Committed 또는 Reapeatable Read 두 가지 중 하나를 선택합니다.) 성능 문제를 해결할 수 있습니다.

 

트랜잭션을 학습하는 입장에서, 4가지 격리 수준이 무엇인지를 알아둘 필요가 있는데 이 부분은 내용이 깊어지는 부분인지라 이 글에서 다루지 않고 다음 글에서 설명하도록 하겠습니다.

 

4. 지속성(Durability)

트랜잭션이 성공적으로 완료된 후 데이터베이스에 반영(commit)한 수행 결과는 어떠한 경우에도 손실되지 않고 영구적이어야 함을 나타내는 특성입니다.

 

시스템에 장애가 발생하더라도, 트랜잭션 작업 결과는 없어지지 않고 데이터베이스 '로그' 등을 통해 데이터베이스에 그대로 남아 있어야 합니다. 언제든 데이터베이스 '로그' 등을 통해 성공된 트랜잭션 내용을 복구할 수 있어야합니다.

 

실제로 MySQL뿐만 아니라, 많은 데이터베이스의 구현에서는 트랜잭션 내역을 하드 디스크에 '로그'로 기록하고 시스템에 이상 발생 시 그 로그를 사용해, 손상된 데이터를 복원하는 것으로 '지속성'을 실현하고 있습니다.


 

이상으로 트랜잭션이 무엇인지, 트랜잭션의 4가지 특성인지 무엇인지를 살펴보았습니다.

 

트랜잭션의 격리 수준을 ANSI에서는 어떻게 정의했고 이 수준에 따라 어떤 현상들이 발생하는 지에 대한 부분은 이 글에 다루기엔 내용이 너무 길어져서 다음 포스팅에서 이어서 설명하도록 하겠습니다. 

'DataBase' 카테고리의 다른 글

[DataBase] 관계형 모델과 집합  (0) 2022.11.20
[DataBase] 트랜잭션의 격리 수준(Isolation Level)  (0) 2022.07.31
Comments