데이터 베이스 트랜잭션

2024. 2. 24. 21:48데이터베이스 & SQL

[ 트랜잭션이란? ]

 데이터 베이스 트랜잭션은 데이터 베이스의 상태를 변화시키는 하나의 논리적 단위 혹은 작업적 단위를 의미한다. 데이터 베이스에서 지원하는 트랜잭션에는 보통 두 가지 모드가 있다. 

 

System Transaction:

보통 autocommit mode로 default 값으로 설정되어있는 것을 의미한다. autocommit을 경험해보면 알겠지만 SQL문 하나마다 해당 작업을 실행하고 성공 후 commit까지 되는게 계속 반복된다. 또한 DB마다 다르지만 보통 select 문에서는 read transaction을 얻고 select를 제외한 SQL문에는 write transaction을 얻는다.

 

User Transaction:

autocommit mode를 false로 하는 JDBC 혹은 @service에서 달아놓는 @Transactional 처럼 하나의 전체 작업이 완전히 이루어지거나 안 이뤄져야 하는 경우에 사용한다. 보통 Begin 문으로 시작해서 마지막에 성공시 commit 실패시 rollback을 하는 방법으로 많이 사용하며, 앞의 System Transaction에 비해서 불필요한 overhead도 없고 해당 명령어 전체의 원자성을 보호해주기 때문에 서비스적으로도 안정성을 줄 수 있어 많이 사용된다.

[ ACID ]

 트랜잭션은 ACID를 지키게 해주는 RDBMS에서의 구현 방법이다.

 

원자성(A:Atomicity) : 원자성(Atomicity)는 매우 중요하다 모든 작업은 한 번에 성공하면 commit, 실패하면 rollback 되어야 한다. 만약에 원자성이 잘 지켜지지 않는 경우를 상상해보자. 돈을 출금할 때에 A에게서 B로 송금을 했는데 A에게 빠져 나간 돈은 적용되고 B에게는 적용이 되지 않는다면, 엄청난 시스템 장애가 될 것이다. 트랜잭션의 원자성은 이런 현상을 막아준다.

 

일관성(C:Consistency): 일관성(Consistency)는 데이터 베이스의 일관성을 유지하고 무결성 제약 조건(integrity constraint)에 맞게 유지시켜야 한다는 것을 의미한다. 

 

격리성(I:Isolation): 격리성(Isolation)은 동시에 적용되는 트랜잭션이 서로에게 영향을 주지 않는 것을 의미한다. 이는 DB에서 세션에 따른 분류와 단계별 락을 통해서 보통 DB가 구현을 하며, 이로 인해 서로 다른 세션에서 아직 commit되지 않은 내용을 조회하는 경우 서로 다른 값을 볼 수 있는 현상이 나타난다.

 

지속성(D: Durability): 지속성(Durability)는 만약 commit이 되었다면 DB에 확실히 반영되었음을 보장해주어야 한다는 것이다. 이건 웬만하면 로그를 통해서 보장을 해준다. 하지만 속도를 향상 시키기 위해서 다른 전략을 쓴다면 꼭 보장되지는 않을 수도 있다.

[ 트랜잭션의 중첩 - savepoint ]

 그렇다면 트랜잭션이 하나의 논리적 단위이면, 트랜잭션 내부에 또 다른 트랜잭션을 놓을 수는 없을까? 결론부터 말하면 가능하다. SQLite나 MySQL을 보면 savepoint라는 명령어가 있다. 해당 명령어를 사용한다면 트랜잭션 내부에 savepoint를 설정해서 해당 부분으로 다시 롤백시킬 수 있다. 이는 예시를 보는 것이 빠르다.

 

MySQL 코드:

CREATE DATABASE ShopppingCart;
USE ShopppingCart;

CREATE TABLE Product
(
	ProductId INT PRIMARY KEY,
    ProductName VARCHAR(40),
    Price INT,
    Quantity INT
);

INSERT INTO Product VALUES(1001, 'Product-1', 1000, 100);
INSERT INTO Product VALUES(1002, 'Product-2', 2000, 150);
INSERT INTO Product VALUES(1003, 'Product-3', 3000, 200);
INSERT INTO Product VALUES(1004, 'Product-4', 4000, 250);

 

이제 다음과 같이 추가적으로 Transcation을 걸고 내부에서 savepoint를 설정해보자. 그리고 실행을 해보면 모든 데이터가 잘 들어가 있음을 확인할 수 있다. 

START Transaction;
	SAVEPOINT SavePoint1;
		INSERT INTO Product VALUES(1005, 'Product-5', 5000, 500);
        	INSERT INTO Product VALUES(1006, 'Product-6', 6000, 600);
	SAVEPOINT SavePoint2;
		INSERT INTO Product VALUES(1007, 'Product-7', 7000, 700);
        	INSERT INTO Product VALUES(1008, 'Product-8', 8000, 800);

 

 그런데, 만약에 일부만 취소를 하고 싶다면? SavePoint를 기준으로 롤백시켜버리면 된다. SavePoint2로 롤백을 시켜보자. 그러면 7-8데이터만 사라진 내부 DB를 관찰할 수 있다. 

ROLLBACK TO SavePoint2;
select * from Product;

 

 하지만 이 상태로는 제대로 DB에 반영이 되지 않는다. 그걸 확인해보는 방법은 다른 세션으로 들어가서 해당 Product 테이블을 조회해보면 알 수 있다. 이 데이터는 commit 하면 해당 데이터에 반영이 된다. 사실 있어서 넣긴 했는데 어디에 쓰는지는 잘 모르겠다. JAVA에서 코드로 이걸 할 일은 없지 않을까?

[ 참고 자료 ]

https://coding-factory.tistory.com/226

스프링 DB 1편 - 데이터 접근 핵심 원리 - 김영한 강의

https://dotnettutorials.net/lesson/savepoint-in-mysql/#:~:text=What%20is%20SAVEPOINT%20in%20MySQL,instead%20of%20the%20entire%20transaction.

VLDB lab