Web/DataBase

[ Database - Intermediate ] - transaction

Hyunseo😊 2023. 7. 18. 15:50

만약 위와같은 이체라는 작업을 한다고 해 봅시다. 이는 원자성있게 동시에 성공했을 때 "이체" 라는 작업이 성공한 것이 될겁니다. 이러한 이체라는 작업은 둘 다 정상 처리돼야만 성공하는 단일 작업이라는 것입니다. 이러한 것을 Transaction이라고 합니다. 즉 논리적인 이유로 여러 SQL문들을 단일 작업으로 묶어서 나눠질 수 없게 만든 것이 transaction입니다.

 

START TRANSACTION;

UPDATE account SET balance = balance - 200000 WHERE id = 'J';
UPDATE account SET balance = balance + 200000 WHERE id = 'H';

COMMIT;

이런식으로 transaction을 만들고 SQL구문을 작성해주고 맨 마지막에 commit을 해주면 되는데, commit은 지금까지 작업한 내용을 DB에 영구적으로(permanently) 저장하라라는 의미이자 transaction의 종료를 나타냅니다.

 

START TRANSACTION
UPDATE account SET balance = balance - 30000 WHERE id = 'J';

select * from account;

ROLLBACK;

그리고 만약 transaction을 하다 rollback이 되면 이는 지금까지 작업들을 모두 취소하고 transaction 이전 상태로 되돌려서 transaction을 종료하겠다는 의미입니다.

 

AUTOCOMMIT

autocommit이란 매우 중요한데, 각각의 SQL문을 자동으로 transaction 처리 해주는 개념입니다. SQL문이 성공적으로 실행되면 자동으로 commit하고, 실행중에 문제가 있었다면 알아서 rollback하게 됩니다. MySQL에서는 default로 autocommit이 enabled되어 있습니다. 그리고 또한 다른 DBMS에서도 대부분 같은 기능을 제공합니다.

 

select @@AUTOCOMMIT;

위를 실행하면 기본적으로 autocmmit이 활성화 되어있는지 확인할 수 있습니다. 이는 기본적으로 1인데, 즉 우리가 기본적으로 CRUD하던건 다 autocomit을 적용시키고 동작하던 것이라고 보면 됩니다. 또한 START TRANSACTION의 실행과 동시에 autocommit은 off되며 COMMIT / ROLLBACK과 함께 transaction이 종료되면 원래 autocommit 상태로 돌아가게 됩니다!

 

보통 자바 코드에서는 위와같이 트랜잭션을 활용하게 됩니다. 저희가 위에서 말한 내용이 그대로 노란색 글씨로 표시되어 있습니다. 여기서 트랜잭션이 시작되면 connection을 쓰레드 풀? 같은데에서 가져와서 setAutoCommit(false)를 해주어야 하고 finally로 다시 true로 만들어 주어야 하는 것입니다! 그 이유는 위에서 말했쬬??

 

그리고 스프링에서 실제로 @Transaction으로 선언적 어노테이션을 AOP를 통해 적용해주면 위의 부가적인 코드는 무시하고 핵심 비지니스 로직에만 집중할 수 있게 됩니다!

 

ACID!!

위에 예시에서 모두 성공하거나, 실패하거나 해야한다 했는데 이를 Atomicity라고 합니다. 즉 All or Nothing으로 동작해야 한다는 것입니다. transaction은 논리적으로 쪼개질 수 없는 작업 단위이기 때문에 내부의 SQL문들이 모두 성공해야 합니다. 

 

여기서 짚고 넘어가야 하는 부분은, commit 실행시에 DB에 영구적으로 저장하는 것은 DBMS가 담당하는 부분이고, rollback 실행 시 이전 상태로 되돌이는 것도 DBMS가 담당하는 부분입니다. 개발자는, 언제 commit하거나 rollback할지만을 챙기면 되는것이죠! 바로 Consistency를 살펴보겠습니다.

 

바로 이전상황에서 추가적으로 J가 H에게 100만원을 이체한다고 해 보겠습니다. 하지만 우리가 account의 check constraint에 balance >= 0을 걸어주었기 때문에 80만 - 100만 = -20만이 되어서는 안됩니다. 이는 INCONSISTENCY가 발생하는 부분입니다. 즉 이러한 작업을 막아야 하는 것이죠.

 

transaction은 DB 상태를 consistent 상태에서 또 다른 consistent 상태로 바꿔줘야 합니다. constraints, trigger등을 통해 DB에 정의된 rules을 transaction이 위반했다면 rollback해야하는 것이죠. 이러한 transaction의 DB rule 위반 여부는 DBMS가 commit이전에 DB에게 알려줍니다. 그 외에 application 관점에서 transaction이 consistent하게 동작하는지는 개발자가 챙겨야하는 부분인 것입니다. 바로 또 Isolation에 대해 알아보겠습니다. 이를 위해 J가 H에게 20만원을 이체할 때 H도 ATM에서 본인 계좌에 30만원을 입금하는 상황을 가정해 보겠습니다.

 

위와같이 그림을 따라가다보면, H가 30만원을 찾은 동작이 씹히게 됩니다. 즉 여러 transaction들이 동시에 실행하니 문제가 발생하게 됩니다. 이러한 것을 방지하기 위해 Isolation을 만족해야 하는데, 여러 transaction들이 동시에 실행될 때도 혼자 실행되는 것처럼 동작하게 만듭니다. 이를 위해 DBMS는 여러 종류의 isolation level을 제공하고 개발자는 islation level 중에 어떤 level로 transaction을 동작시킬지 설정할 수 있습니다. 즉 concurrency control의 주된 목표가 isolation이 되는 것입니다.

 

즉 나중에 알아보겠지먄 isloation level이 높아지면 concurrency가 제한되어 성능이 안좋아지지만 transaction이 그만큼 안전히 실행될 수 있고, level이 낮아지면 그만큼 성능이 좋아지지만 transaction에서 동시성을 관리하기 위해 관리하고 생각해야 할 부분이 많아질 수 있습니다. 바로 Durability를 알아보겠습니다. 

 

이는 commit된 transaction은 DB에 영구적으로 저장된다는 것입니다. 즉, DB에 문제(power fail or DB crash)가 생겨도 commit된 transaction은 DB에 남아 있다는 얘기입니다. 기본적으로 transaction의 durability는 DBMS가 보장합니다. 이는 당연하지만 주로 비휘발성 메모리 HDD, SDD에 저장하는 것을 말하는 거겠죠??