일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
- 반효경
- Git
- 갤럭시 S24
- Extendable hashing
- concurrency control
- 시그널 핸들러
- 운영체제
- SDK
- 개발남노씨
- 코딩테스트 [ ALL IN ONE ]
- 프로세스 주소 공간
- 김영한
- 데이터베이스
- 쉬운코드
- 인터럽트
- 커널 동기화
- 시스템프로그래밍
- 네트워크
- B tree 데이터삽입
- recoverability
- 백엔드
- 코딩애플
- BreadcrumbsComputer-Networking_A-Top-Down-Approach
- 트랜잭션
- 운영체제와 정보기술의 원리
- CPU 스케줄링
- vite
- 쉬운 코드
- 온디바이스AI
- SQL
- Today
- Total
티끌모아 태산
DB - transaction 본문
이번시간에는 트랜잭션과 관련된 중요한 속성들인 ACID에 대해서 공부해 보겠습니다.
Transaction
다음과 같이 간단한 예제를 통해서 개념에 대해 배워보겠습니다. J가 H에게 20만원을 이체한다면 각자의 계좌는 어떻게 변경돼야 할까요?

현재 상태를 살펴보면, 먼저 J는 계좌에 100만원이 있고 H는 200만원이 있다고 가정해 보겠습니다.

J가 H에게 20만원을 보내게 되면 J는 100만원에서 80만원이 되고 H는 200만원에서 최종적으로 220만원이 됩니다.

그러면 이 과정을 SQL문으로 표현하면 어떻게 될까요? account 테이블에서 잔액을 의미하는 balance 를 업데이트 해주는 과정입니다.
// J part
UPDATE account SET balance = balance - 200000 WHERE id = 'J';
// H part
UPDATE account SET balance = balance + 200000 WHERE id = 'H';
여기서 중요한 점은 이 SQL 두개가 모두 성공을 해줘야 이체가 됐다고 할 수 있습니다. 이게 무슨 말이냐면 둘 중 하나라도 실패한다면 이는 이체가 완료됐다고 할 수 없습니다. 예를들어, J는 20만원을 송금해서 총 80만원이 남았는데, H는 이를 받지 못해 여전히 200만원이라면 이는 이체가 실패한 것입니다. 반대로 H는 20만원을 받아서 총 220만원이 됐는데, J는 20만원을 보낸적이 없다면 이 또한 이상한 일입니다. 결국 이체가 성공 했다는 것은 두 개의 쿼리가 모두 성공했을 때만 이루어 집니다.


그림과 같이 둘 다 정상 처리돼야만 성공하는 단일 작업 입니다. 즉 두개가 모두 성공적으로 처리돼야만 작업이 완성됐다고 할 수 있습니다. 이것을 우리는 트랜잭션이라고 부릅니다.

- 단일한 논리적인 작업 단위 - 논리적인 이유로 여러 SQL문들을 단일 작업으로 묶어서 나눠질 수 없게 만든 것을 우리는 transaction 이라고 합니다.

- 그래서 트랜잭션의 SQL문들 중에 일부만 성공해서 DB에 반영되는 것은 발생하지 않습니다. 모두 성공해야 합니다.
이제 MySQL에서 transaction을 사용해서 구현하는 것을 보겠습니다.
SELECT * FROM account;

J가 H에게 20만원 이체한 것을 트랜잭션으로 구현해 보겠습니다.
START TRANSACTION; // DBMS에 transaction을 시작한다고 알려주는 것.
UPDATE account SET balance = balance - 200000 WHERE id = 'J';
UPDATE account SET balance = balance + 200000 WHERE id = 'H';
// 이는 지금까지 작업한 내용을 DB에 영구적으로 저장하라는 뜻, transaction을 종료한다.
COMMIT; // 에러가 발생하지 않으면 커밋한다.
*COMMIT - DB에 영구적으로 저장, 트랜잭션을 종료. 다시 SELECT문으로 account table을 조회했더니 다음과 같이 결과가 나왔습니다.
SELECT * FROM account;

그럼, 추가로 J가 H에게 30만원 이체한 것을 transaction으로 구현해 보겠습니다.
START TRANSACTION;
UPDATE account SET balance = balance - 300000 WHERE id = 'J';
이렇게만 작성하고 account table을 조회하면 다음과 같습니다. J에서는 30만원이 빠져나갔는데 H는 30만원을 받지 못했습니다. 이런 상태에서 ROLLBACK을 수행하면 어떻게 될까요?
ROLLBACK;
롤백은 이전상태로 복구하는 것을 의미합니다. 즉, 지금까지 작업들을 모두 취소하고 transaction 이전 상태로 되돌린다. 그리고 transaction을 종료한다는 뜻입니다. 그럼 추가적으로 다음 AUTOCOMMIT에 대해 배워보겠습니다.
SELECT @@AUTOCOMMIT; // 현재 AUTOCOMMIT이 활성화 되어있는지 여부를 확인하기 위함. 보통 1은 활성화
- 각각의 SQL문을 자동으로 transaction 처리 해주는 개념입니다.
- SQL문이 성공적으로 실행하면 자동으로 commit 한다.
- 실행 중에 문제가 있었다면 알아서 rollback 한다.
- MySQL에서는 default로 autocommit이 enabled 되어 있다.
- 다른 DBMS에서도 대부분 같은 기능을 제공한다.

그럼 account 테이블에 W라는 id를 가진 계좌에 100만원을 insert 해보겠습니다.
insert into account values ('W', 1000000);
그럼 autocommit이 enabled된 상태이기 때문에 insert문을 싱행하면 자동으로 commit이 되면서 account 테이블에 ('W', 1000000) .데이터가 영구적으로 저장됩니다.

만약 오토커밋을 비활성화 하고싶으면?
SET autocommit = 0;
오토 커밋을 비활성화 한 후 DELETE 문을 실행하면 쿼리가 성공적으로 처리됐음을 확인할 수 있습니다. 그럼 이때 rollback이 가능할까요? 네, autocommit을 off 했기 때문에 자동으로 커밋되지 않아 rollback을 할 수 있어 이전 상태로 돌아갈 수 있습니다.

그럼 rollback을 해보겠습니다. 그럼 아래와 같이 두 개의 계좌가 다시 복원이 됐음을 알 수 있습니다.

START TRANSACTION;
UPDATE account SET balance = balance - 200000 WHERE id = 'J';
UPDATE account SET balance = balance + 200000 WHERE id = 'H';
COMMIT;
mySQL 위 쿼리 문에서 START TRANSACTION이 실행됨과 동시에 autocommit은 off가 됩니다. 그래서 COMMIT이라는 명령어를 수행해야지 DB상에 영구적으로 저장될 수 있습니다. 그리고 COMMIT / ROLLBACK을 하게 되면 transaction이 종료되면서 원래 autocommit 상태로 돌아가게 됩니다.
일반적인 transaction 사용 패턴
- 트랜잭션을 시작(begin)한다.
- 데이터를 읽거나 쓰는 등의 SQL문들을 포함해서 로직을 수행한다.
- 일련의 과정들이 문제없이 동작했다면 transaction을 commit한다.
- 중간에 문제가 발생했다면 transaction을 rollback한다.
ACID
트랜잭션이 어떤 속성을 지녀야하는지를 나타내는 개념들입니다,
- Atomicity
- Consistency
- Isolation
- Durability
Atomicity
- ALL or NOTHING
- transaction은 논리적으로 쪼개질 수 없는 작업 단위이기 때문에 내부의 SQL문들이 모두 성공해야합니다.
- 중간에 SQL문이 실패하면 지금까지의 작업을 모두 취소하여 아무 일도 없었던 것처럼 rollback 합니다.

Consistency
만약 J의 계좌에 80만원이 있는데 100만원을 H에게 보낸다고 가정해 보겠습니다. 그럼 J는 -20만원이 되고 H는 320만원이 됩니다. 이때, 만약 우리가 테이블을 생성할 때 check(balance >= 0) 이라는 조건을 걸어줬다면 J쿼리문은 실패하게 됩니다. 즉 INCONSISTENCY가 되는 것이죠.

그래서 이 쿼리문은 아무런 의미가 없기 때문에 결국에는 ROLLBACK을 해주어야 합니다.

이처럼 데이터베이스의 일관성을 유지시켜주는 속성을 Consistency라고 합니다.

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

J측에서 먼저 계좌에 얼마가 있는지 확인하기 위해 read 하여 100만원임을 확인합니다. 그후 20만원을 송금하고 계좌에 다시 80만원이라고write합니다. 그럼 H는 계좌에 얼마가 있는지 확인하기 위해 read하면 200만원이 있다고 나옵니다. 그리고 나서 20만원을 받은 금액을 write하려고하는데, 이때 즉 write 바로 직전에 내가 ATM기에서 30만원을 넣는 트랜잭션이 일어났다고 해보겠습니다. 그러면 다시 두 번째 트랜잭션을 위해 read 하면 H계좌에 200만원이 있다고 하며 30만원을 더 한 후 230만원이라고 write하고 second transaction이 종료됩니다. 그 후 first transaction을 처리합니다. 하지만 first는 230만원이 계좌이 들어있는지 모릅니다. (update를 하지 않았기 때문에 )read했던 시점이 200만원이기 때문에 여전히 200만원이라 생각하고 20만원을 write한 후 terminate first transaction합니다. 결국 여러 트랜잭션으로 인해서 문제가 발생한 것입니다.
- 여러 transaction들이 동시에 실행될 때도 혼자 실행되는 것처럼 동작하게 만듭니다.
- DBMS는 여러 종류의 isolation level을 제공합니다.
- 개발자는 isolation level 중에 어떤 level로 트랜잭션을 동작시킬지 설정할 수 있습니다.
- consistency control의 주된 목표가 isolation입니다.
생각해볼 점은 isolation의 level이 높으면 높을 수록 보다 엄격하게 격리를 시켜서 다른 트랜잭션으로 부터 영향을 받을 가능성이 줄어듭니다. 하지만 너무 엄격하면 동시성이 떨어져서 성능이 저하됩니다. 반대로 level이 너무 낮으면 동시성은 높아지지만 여러 트랜잭션의 방해로 인해서 이상한 결과가 초래될 수 있습니다.-> trade-off
Durability(영존성)
커밋을 통해 트랜잭션이 완료되면 그 트랜잭션은 DB에 영구적으로 저장됩니다. 이것을 durability라고 합니다.

- commit된 트랜잭션은 DB에 영구적으로 저장됩니다.
- 즉, DB 시스템에 문제가 생셔도 commit된 트랜잭션은 DB에 남아 있습니다.
- 영구적으로 저장한다라고 할때는 일반적으로 비휘발성 메모리(HDD, SSD...)에 저장함을 의미합니다.
- 기본적으로 트랜잭션의 durability를 DBMS가 보장합니다.
트랜잭션을 어떻게 정의해서 쓸지는 개발자가 정하는 것입니다. 그래서 ACID와 관련해서 개발자가 챙겨야하는 부분들이 있습니다. 즉 DBMS가 모든것을 다해주는 것은 아닙니다. 지금까지 트랜잭션의 개념에 대해서 배워 보았습니다. 감사합니다.
'CS 지식 > 데이터베이스' 카테고리의 다른 글
트랜잭션 - concurrency control 기초(1) (1) | 2023.12.08 |
---|---|
DB - indexing(2) (1) | 2023.12.05 |
DB - indexing(1) (2) | 2023.12.05 |