데이터베이스, RDBMS는 트랜잭션이라는 기능을 제공합니다. INSERT나 UPDATE 명령으로 데이터를 추가, 갱신할 때 이미 트랜잭션 명령을 사용하고 있었습니다, 하지만 우리는 사용하면서 지금까지 특별히 의식할 필요가 없었습니다. 그 이유는 자동 커밋(auto commit)이라 불리는 기능이 동작했기 때문입니다. 이런 트랜잭션으로 어떤것을 할 수 있는지 트랜잭션은 무엇인지 알아보도록 하겠습니다.
트랜잭션
트랜잭션에 대해 알아보기에 앞서 정규화에 의해 분할된 주문 테이블과 주문 상품 테이블의 관계를 생각해 보겠습니다.
주문
주문 상품
주문 테이블과 주문 상품 테이블 사이에는 의존관계가 존재합니다. 주문 테이블에 행이 존재한다면 주문 상품 테이블에는 적어도 하나의 행이 존재해야 합니다. 그렇지 않다면 주문한 상품이 없는데도 주문이 된 상태가 됩니다. 보통은 하나 이상의 상품을 주문하므로 주문이 발생하면 주문 테이블과 주문 상품 테이블 모두 행이 추가됩니다.
발주 처리
주문이 발생 했을 때 어떻게 처리되는지에 대해 알아보겠습니다. 간단히 말하자면 발주처리에 관한 것으로, 먼저 주문 번호를 지정해야 합니다. 이때 기존 주문과 구분되는 주문번호를 발행하는 처리가 필요합니다.
자동 증가를 사용하면 자동적으로 번호가 부여되지만 그렇지 않은 경우에는 ‘번호 중 가장 큰 값을 SELECT 명령으로 가져와 그 값에 1을 더한다’ 라는 처리가 필요합니다. 최대값은 MAX()로 검색할 수 있으므로 ‘MAX + 1’이라 할 수도 있습니다.
번호를 발행 받았다면 해당 번호를 키로 삼아 INSERT가 이루어집니다. 주문 테이블에는 INSERT 한번, 주문 상품 테이블에는 주문된 상품 수만큼 INSERT 명령이 실행됩니다. 중요한 것은 복수의 테이블에 INSERT 되므로 실행되는 명령은 최소 두 번이라는 것입니다.
INSERT INTO 주문 VALUES(4, '2023-02-09', 1);
INSERT INTO 주문상품 VALUES(4, '0003', 1);
INSERT INTO 주문상품 VALUES(4, '0004', 2);
이 상황에서 INSERT 명령이 네트워크 장애등 특정 원인으로 에러가 발생해 정상적으로 수행되지 않은 경우를 가정해 보겠습니다.
트랜잭션 기능을 사용하지 않을 때에는 문제없이 실행된 INSERT 명령을 실행 전으로 되돌릴 수 없으므로, 따로 DELETE 명령을 실행해 해당 INSERT내용을 지워야 합니다. 즉, INSERT INTO 주문상품 VALUES(4, '0004', 2); 에서 에러가 발생했다고 치면, 앞서 실행한 두 개의 INSERT 명령에 의해 추가된 데이터를 DELETE 명령으로 삭제하는 처리가 필요합니다. 이런 작업이 만약 한번에 40개의 INSERT를 처리하는 일이라 가정하면.. 정말 번거롭고 시간이 오래걸리는 작업이 될 것 입니다.
롤백 커밋
앞서 제시한 상황처럼 몇 단게로 처리를 나누어 SQL 명령을 실행하는 경우에 트랜잭션을 자주 사용합니다. 트랜잭션을 사용해서 데이터를 추가한다면 에러가 발생해도 트랜잭션을 롤백(Rollback)해서 종료할 수 있습니다. 롤백하면 트랜잭션 내에서 행해진 모든 변경 사항을 없었던 것으로 할 수 있습니다. 또한 아무런 에러가 발생하지 않고 정상적으로 수행되었다면 변경사항을 적용하고 트랜잭션을 종료하는데 이 때 커밋(commit)을 사용합니다.
💡 트랜잭션을 콜백하면 변경한 내용이 적용되지 않습니다!
- 자동 커밋
트랜잭션을 사용해서 데이터를 추가할 때는 자동 커밋을 꺼야 합니다. Mysql을 기준으로 클라이언트(여기서는 Mysql workbench나 shell을 통해 데이터베이스를 조작하는 입장을 말합니다)에서 명령을 실행할 때는 자동 커밋이 켜져 있는 상태 입니다.
INSERT, UPDATE, DELETE가 처리될 때 마다 트랜잭션은 암묵적으로 자동커밋 상태로 되어 있습니다.
자동 커밋을 끄기 위해서는 명시적으로 트랜잭션의 시작을 선언할 필요가 있습니다. 트랜잭션을 싲가할 때는 START TRANSACTION 명령을 사용합니다.
또한 앞서 언급한 것 처럼 트랜잭션을 종료할 때는 변경된 사항을 적용한 후에 트랜잭션을 종료하는 커밋 과 변경 사항을 적용하지 않고 종료하는 롤백 의 두가지 방식이 있습니다. 커밋할 때에는 COMMIT 명령을 사용합니다. 롤백은 ROLLBACK 명령을 사용합니다.
트랜잭션 내에서 실행된 SQL 명령은 임시 데이터 영역에서 수행되다가, COMMIT 명령을 내리면 임시 데이터 영역에서 정식 데이터 영역으로 변경이 적용 됩니다. ROLLBACK 명령을 내리면 임시 데이터 영역에서의 처리는 버려지게 됩니다.
Undo Log란? Mysql에서는 임시 테이블 스페이스에 있는 영역을 Undo Log라고 칭합니다. 이 Undo Log는 사용자가 정의한 임시 테이블의 데이터를 수정또는 변경하는 트랜잭션에 사용됩니다. 실행이 취소된 로그 레코드의 집합으로 Transaction 실행 후 Rollback 명령어를 실행 시, Undo Log를 참조해 이전 데이터로 복구할 수 있도록 로깅 해놓은 영역입니다.
앞서 설명한 발주 처리 과정을 트랜잭션을 사용해 진행해보도록 하겠습니다.
에러가 발생하지 않은 경우로, 트랜잭션은 COMMIT을 이용해 종료합니다.
START TRANSACTION;
INSERT INTO 주문 VALUES(4, '2023-02-09',1);
INSERT INTO 주문상품 VALUES(4, '0003',1);
INSERT INTO 주문상품 VALUES(4, '0004',2);
COMMIT;
위와 같이 트랜잭션 명령어로 시작해서 SQL 명령을 실행하고, COMMIT 또는 ROLLBACK 명령으로 트랜잭션을 종료하는 일련의 과정을 트랜잭션을 걸어서 실행한다 또는 트랜잭션 내에서 실행한다 라고 말합니다.
트랜잭션 사용
발주처리와 같은 데이터 등록 처리 과정에서는 대부분 트랜잭션 내에서 여러 개의 SQL 명령을 실행하게 됩니다. 하지만 하나의 명령에 트랜잭션을 걸어 실행하는 것은 별로 의미가 없습니다.
트랜잭션 내에서 실행하는 복수의 SQL 명령은 세트(Set 집합) 단위로 유효/무효가 됩니다.
다시 말해, 반드시 세트로 실행하고 싶은 SQL 명령을 트랜잭션에서 하나로 묶어 실행한다는 것 입니다. 또 ROLLBACK은 에러가 발생한 경우 변경사항이 적용되지 않도록 하는 목적으로 주로 사용하지만, 만약 사용자가 에러가 발생하지 않은 상황에서 일부러 ROLLBACK을 호출한다면 변경한 내용은 파기됩니다. 반대로 에러가 발생하더라도 COMMIT을 하면 문제없이 실행된 SQL 명령의 변경사항은 데이터베이스에 그대로 반영됩니다.
트랜잭션의 ACID
트랜잭션은 흔히 ACID라고 하는 다음의 네가지 성질로 설명이 됩니다.
트랜잭션을 설명할 때 대표적으로 많이 드는 예시인 이체과정을 예시로 설명하겠습니다.
- Atomicity(원자성) : 이체 과정 중에 트랜잭션이 실패하게 되어 예금이 사라지는 경우가 발생해서는 안되기 때문에 DBMS는 완료되지 않은 트랜잭션의 중간 상태를 데이터베이스에 반영해서는 안됩니다.( 예를 들어, 이체 과정 중 송금은 완료 되었으나 송금된 금액이 수신자의 통장에 반영이 되지 않았는데 이런 중간상태를 반영하지 않는 것 입니다.) 즉, 트랜잭션의 모든 연산들이 정상적으로 수행 완료 되거나 아니면 전혀 어떤 연산도 수행되지 않은 상태를 보장해야 하는것을 말합니다. 쉽게 ‘all or nothing’이라는 특성으로 설명됩니다.
- Consistency(일관성) : 고립된 트랜잭션의 수행이 데이터베이스의 일관성을 보존해야 한다. 라고 설명하고 있습니다 쉽게 말해 성공적으로 수행된 트랜잭션은 정당한 데이터들만을 데이터베이승 반영해야 한다 라고 말할 수 있습니다. 트랜잭션의 수행을 데이터베이스 상태 간의 전이(transition)로 봤을 때, 트랜잭션 수행 전 후의 데이터베이스 상태는 각각 일관성이 보장되는 서로 다른 상태가 됩니다. 제가 예시로 든 이체 과정에서 두 계좌 잔고의 합은 이체 전 후가 같아야 한다는 사항과 같은 비명시적인 일관성 조건들도 있습니다.
- 만약 계좌의 잔고는 0 이상이여야 한다라는 제약 조건이 걸려있다고 가정하겠습니다.
이런 상황에서 그림과 같이 현재 잔고에서 -1000000을 수행했을 때 통장의 잔고가 -20만원이 된다고 가정하겠습니다.CREATE TABLE account( ..., balance INT, check (balance >= 0) )
잔고가 -20만원이 된 상황에서 이미, account 테이블의 제약조건을 어기게 되는 상황이니, 더 이상 트랜잭션이 진행되어도 소용이 없기 때문에, ROLLBACK을 시켜주게 됩니다.
- constaraints, trigger 등을 통해 DB에 정의된 rules을 transaction이 위반했다면 rollback 해야 합니다.
- transaction이 DB에 정의된 rule을 위반했는지는 DBMS가 commit 전에 확인하고 알려줍니다.
- Isolation(독립성) : 여러 트랜잭션이 동시에 수행되더라도 각각의 트랜잭션은 다른 트랜잭션의 수행에 영향을 받지 않고 독립적으로 수행되어야 한다. 즉, 한 트랜잭션의 중간 결과가 다른 트랜잭션에게는 숨겨져야 한다는 의미 입니다.
- 위의 그림에서 J가 H에게 20만원을 이체할 때, H도 ATM에서 본인 계좌에 30만원을 입금한다는 상황을 가정하고 설명하겠습니다.
- J가 H에게 20만원을 이체 했기 때문에 J의 계좌에서 20만원이 차감됩니다.
- H의 계좌에서 200만원이 있다는 것을 확인합니다, 이 때 H가 30만원을 자신의 계좌로 입금합니다. 그 후 계좌의 잔고를 read하면, 230만원이 확인 됩니다.(30만원 입금 트랜잭션 종료)
- 이어서 J가 H에게 20만원을 이체하는 트랜잭션이 이어서 수행됩니다. 이 때 이 트랜잭션에서 H가 자신의 계좌로 30만원을 이체 했다는 사실을 알 수 없습니다. 왜냐하면 J가 H한테 송금을 하는 트랜잭션의 시작에서 이미 H의 계좌가 200만원인 것을 read를 통해서 읽어 왔기 때문에, H가 자신의 계좌에 추가로 돈을 입금했다는 사실을 알 수 없습니다.
- 결국 H는 자신의 계좌에 30만원을 입금하고, J로 부터 20만원을 송금 받았음에도 220만원밖에 남지 않게 되는 결과가 발생합니다.
앞서 설명한 내용처럼 Isolation은 여러 특성을 가지고 있습니다.
- Isolation은 여러 transaction들이 동시에 실행될 때도 혼자 실행되는 것처럼 동작하게 만들어야 합니다.
- DBMS는 여러 종류의 isolation level을 제공합니다.
- isolation level을 높임으로써, 동시성 상황에서 다른 트랜잭션으로부터 해당 트랜잭션이 받을 영향을 줄일 수 있습니다. 하지만 엄격한 격리에 따라 동시에 실행 할 수 있는 동시성이 떨어지게 때문에 DB의 성능이 저하되게 됩니다.
- 반대로 isolation level을 낮게 설정하면, DB의 성능은 유지될 수 있지만 다른 트랜잭션으로 부터 영향을 받을 가능성이 커지기 때문에 예기치 못한 결과를 발생 시킬 수도 있습니다.
- 개발자는 isolation level 중에 어떤 level로 transaction을 동작시킬지 설정할 수 있습니다.
- Durability(지속성) : 트랜잭션이 성공적으로 완려되었으면 결과는 영구히 반영되어야 한다는 뜻입니다.
- 즉, DB system에 문제(power fail or DB crash)가 생겨도 commit된 transaction은 DB에 남아 있는다.(한번 commit된 트랜잭션은 DB에 쓰여져 있기 때문입니다.)
- ‘영구적으로 저장한다’라고 할 때는 일반적으로 ‘비휘발성 메모리(HDD,SDD..)에 저장함’을 의미합니다.
- 반면에, 변경은 됐지만 아직 commit 되지 않은 내용들은 비휘발성 메모리에 임시로 저장될 수도 있고 메인 메모리에 저장될 수도 있습니다.(자세한 내용은 각 DBMS 메뉴얼을 참고해주세요)
- 기본적으로 transaction의 durability는 DBMS가 보장합니다.
마치며
트랜잭션에 대해 DBMS에서 자동으로 설정해준다고 생각하고 그에 대해 자세히 알지는 못했었습니다. 이번 기회를 통해 트랜잭션의 ACID성질에 대해서도 다시한번 예제를 통해 학습하고, 추후 더 깊은 이해를 위해 참고자료에 있는 https://d2.naver.com/helloworld/407507 를 통해 학습하려 합니다.
참고 자료
https://www.youtube.com/watch?v=sLJ8ypeHGlM
SQL 첫걸음
'프로그래밍 > 데이터베이스' 카테고리의 다른 글
데이터베이스 선택에 대한 고민 (0) | 2023.09.21 |
---|---|
CAP 이론? (0) | 2023.02.17 |