UPDATE account SET balance - balance - 200000 WHERE id = 'j';
UPDATE account SET balance - balance + 200000 WHERE id = 'H';
두 SQL은 모두 성공해야 이체라는 작업이 정상적으로 성공했다고 볼 수 있다.
즉 둘 다 정상 처리되어야만 성공하는 단일 작업으로 볼 수 있다.
이렇게 SQL 문이 모두 성공해야만 작업이 의미가 있는 작업을 데이터베이스에서 트랜잭션이라고 부른다.
트랜잭션
- 단일한 논리적인 작업 단위를 의미
- 어떤 논리적인 목적으로 여러 SQL 문들을 단위의 작업으로 묶어서 나누어질 수 없게 만든 것
트랜잭션 내부의 인해 SQL 문들 중에 일부만 성공해서 DB에 반영되는 일은 일어나지 않는다.
논리적으로 하나의 단일 작업으로 묶여서 모두 성공해야만 의미가 있는 게 바로 이 트랜잭션 이기 때문에 일부만 성공해도 DB에 반영되지 않기 때문이다. 모두 성공해야만 DB에 반영된다.
START TRANSACTION;
UPDATE account SET balance - balance - 200000 WHERE id = 'j';
UPDATE account SET balance - balance + 200000 WHERE id = 'H';
COMMIT;
여기서 COMMIT은 지금까지 작업한 내용을 DB에 영구적(permanently)으로 저장하고 트랜잭션을 종료한다는 의미를 가진다.
autocommit
mysql에서는 autocommit이 활성화 되어 있다. 하나의 sql문을 실행하면 자동 커밋이 되게끔 하는 것이다.
autocommit을 비활성화 한 후 sql문을 동작시키고 rollback을 하면 commit이 자동으로 되지 않았기 때문에 sql문 실행 이전으로 돌아갈 수 있다.
mysql에서 autocommit은 활성화가 default인데 transaction에서는 rollback이 왜 가능한걸까?
이유는 start transaction을 실행하면 autocommit이 자동 비활성화 되기 때문이다.
commit/rollback과 함께 transaction이 종료되면 원래 autocommit 상태로 돌아간다.(원래 off였으면 off , on이였으면 on)
✅ 일반적인 transaction 사용 패턴
1. transaction 시작
2. 데이터를 읽거나 쓰는 등의 SQL문들을 포함한 로직 수행
3. 일련의 과정들이 문제없이 동작했다면 commit / 중간에 문제가 발생했다면 rollback
개발할 때
일반적으로 개발을 할 때 직접 DB 서버에 SQL문을 입력하지는 않기 때문에 트랜잭션 명령어도 프로그래밍 언어로 작성하게 된다.
아래 코드는 java의 예시코드다.
public class BookmarkGroupRepository {
public int transfer(String fromId, String toId, int amount) {
Connection connection = null;
int rowCount = 0;
try {
connection = DataBaseConnector.getConnection();
connection.setAutoCommit(false); // 트랜잭션 시작
... // 로직 구현
connection.commit();
} catch (SQLException e) {
connection.rollback();
} finally {
connection.setAutoCommit(true);
}
}
여기서 실제 로직 관련 부분은 ... // 로직 구현 밖에 없다. 나머지 부분은 다 트랜잭션 관련 부분이다.
이런 부가적인 코드들이 반복적으로 같이 있으면 보기가 어렵다.
만약 스프링이나 스프링 부트를 이용해서 @Transactional이라는 어노테이션을 붙이면 트랜잭션과 관련된 부가적인 코드들을 숨기고 로직 부분만 작성하게 만들 수 있다.
ACID
ACID는 각각 트랜잭션의 속성을 나타낸다.
원자성(Atomicity)
All or Nothing
트랜잭션이 논리적으로 쪼개질 수 없는 작업 단위를 의미하기 때문에 트랜잭션 내부에 있는 모든 작업은 성공해야 한다.
중간에 SQL문이 실패하면 지금까지의 작업을 모두 취소하여 아무 일도 없었던 것처럼 rollback 한다.
그래서 데이터베이스의 상태가 일관적일 수 있도록 유지를 해줘야 한다.
✅ DBMS 담당
- commit 실행 시 DB에 영구적으로 저장하는 작업
- rollback 실행 시 이전 상태로 되돌리는 작업
✅ 개발자 담당
- 언제 commit/rollback 할지 정해주는 작업
일관성(Consistency)
트랜잭션이 실행을 성공적으로 완료하면 언제나 일관성 있는 데이터베이스 상태로 유지하는 것
만약 잔액은 -가 될 수 없다는 제약사항이 있다고 가정하자.
남은 잔액보다 더 큰 금액을 출금하면 일관성 유지를 위해 rollback을 해주어야 할 것이다.
transaction은 DB 상태를 일관성있는(consistent) 상태에서 또 다른 일관성있는(consistent) 상태로 바꿔줘야 한다.
constraints, trigger 등을 통해 DB에 정의한 rules을 trasaction이 위반했다면 rollback 해야 한다.
transaction이 DB에 정의된 rule을 위반했는지 DBMS가 commit 전에 확인하고 알려준다.
✅ DBMS 담당
- DB에 정의된 rule을 확인
✅ 개발자 담당
- DB에 정의된 rule 외에도 application 관점에서의 transaction이 consistent 하게 동작하는지 확인
고립성(Isolation)
간소화: r1(J) w1(J) r1(H) r2(H) w2(H) c2 w1(H) c1
- 계좌이체를 위해 J 계좌에서 20만원을 빼고, H 계좌에 20만원을 입금한다.
- 이때 H가 본인 계좌에 같은 타이밍에 30만원을 입금한다면, 시스템이 충돌날 수 있다.
- J입장에서의 로직은 H의 잔액을 읽고 20만원을 더해주면 끝인데 H의 잔액을 읽었을 때 H가 본인의 통장의 잔액을 읽고 입금했다고 하면 H의 입금이 끝나고 J의 입금을 끝내면 J는 H가 입금을 한 여부를 모르기 때문에 H 기존 잔액 + J의 입금 금액만이 결과로 나올 수 있게 되는 것이다.
여러 transaction들이 동시에 실행될 때도 혼자 실행되는 것처럼 동작하게 만든다.
너무 엄격한 isolation은 DBMS의 성능이 줄어들게 하기 때문에 DBMS는 여러 종류의 isolation level을 제공한다.
level 이 높을 수록 엄격하게 격리를 시켜 다른 트랜잭션으로부터 영향 받을 가능성이 줄지만 동시성이 떨어지게 되어 성능이 낮아질 수 있고
낮으면 낮을 수록 격리를 시키지 않기 때문에 성능은 좋아지지만 다른 트랜잭션으로부터 영향 받을 가능성이 커지기 때문에 결과가 이상할 가능성이 커지게 된다.
개발자는 isolation level 중 어떤 level로 transaction을 동작시킬지 설정할 수 있다.
default로 설정은 되어 있지만 상황에 따른 튜닝이 필요하다.
concurrency control의 주된 목표가 isolation이다.
지속성/영속성(Durability)
commit된 transaction은 DB에 영구적으로 저장된다.
DB system에서 문제(power fail or DB crash)가 생겨도 commit된 transaction은 DB에 남아 있는다.
영구적으로 저장한다라고 할 때는 일반적으로 비휘발성 메모리(HDD,SSD..)에 저장함을 의미한다.
기본적으로 transaction의 durability는 DBMS 가 보장한다.
참고사항
1. 트랜잭션을 어떻게 정의해서 쓸지는 개발자가 정한다.
- 구현하려는 기능과 ACID 속성을 이해해야 transaction을 잘 정의할 수 있다.
2. 트랜잭션의 ACID 와 관련해서 개발자가 챙겨야 하는 부분들이 있다.
- DBMS가 모두 알아서 해주는 것이 아니다.
유튜브 - 쉬운코드의 데이터베이스 트랜잭션(transaction)을 아십니까? 그리고 트랜잭션의 매우 중요한 속성들인 ACID를 아십니까? 모르신다면 들렀다 가시지요
를 보고 정리한 내용입니다.
'knowledge > computer science' 카테고리의 다른 글
Transaction isolation level (0) | 2024.06.19 |
---|---|
concurrency control 기초: schedule과 serializability (0) | 2024.06.19 |
[성능 테스트] Artillery 시나리오, 파라미터 (0) | 2024.06.07 |
[성능 테스트] Artillery 설치 (0) | 2024.06.06 |
[성능 테스트] 필요한 배경 지식 (0) | 2024.06.03 |