[Database] 트랜잭션(Transaction), ACID 및 Isolation Level
트랜잭션 (Transaction)
하나의 로직을 처리하는 SQL 질의들을 집합으로 묶어, 도중에 예외가 발생할 경우 Rollback 처리를, 모두 성공할 경우 Commit 처리하는 실행 단위이다.
쇼핑몰에서 주문할 때의 과정을 생각해보자. (주문 요청 -> 결제 처리 -> 성공 시 주문 완료 데이터 저장)
만약에 다음 과정에서 결제는 완료되었는데 주문이 들어가지 않았다면 사용자 입장에서는 어이가 없을 것이다.
또 결제 처리는 실패되었는데 주문 완료 데이터는 저장되어있다면 해당 데이터베이스는 더이상 믿을 수 없게 된다.
즉, 성공하려면 모두 성공하고, 실패하려면 모두 실패해야 한다.
트랜잭션 작성 예시 (MySQL)
BEGIN
START TRANSACTION; -- 트랜잭션 시작. rollback이나 commit을 만날 때 까지 트랜잭션
-- 일련의 작업 ...
IF 조건 > THEN -- ROLLBACK
ROLLBACK;
-- 일련의 작업 ...
COMMIT; -- 트랜잭션 성공
END
Transaction ACID
데이터베이스의 트랜잭션에 대해서 데이터의 정확성과 안전성을 보장하고 데이터 오염을 방지하기 위한 성질이다.
- A(Atomicity) : 원자성
- 트랜잭션의 작업은 모두 성공하거나, 모두 실패하여야 함 트랜잭션과 관련된 작업들이 부분적으로 실행되다가 중단되지 않는 것을 보장하는 능력.
- C(Consistency) : 일관성
- 트랜잭션이 실행을 성공적으로 완료될 시 일관적인 DB상태를 유지해야 한다.
- 트랜잭션이 일어난 이후 자료형, 길이, 관계 등 데이터베이스 규칙이 변하지 않아야 한다.
- I(Isolation) : 격리성
- 일련의 트랜잭션을 작업 중 다른 트랜잭션의 연산 작업이 끼어들지 못하도록 격리시키는 것
- 트랜잭션은 다른 트랜잭션으로부터 독립성을 유지되어 있어야 하며, 같은 자원을 사용한다면 동시에 실행될 수 없음
- 철수의 작업 동안에서는 영희의 작업은 기다려야 함
- D(Durability) : 지속성
- 트랜잭션에 대한 로그가 남아야 하는 성질로 런타임 오류나 시스템 오류가 발생하더라도, 해당 기록은 영구적이어야 하는 것.
- commit이 되었으면 런타임 오류로 서버가 재실행 되더라도, 그 데이터는 그대로 유지가 되어야 되는 것.
Isolation Level
ACID의 성질 중 Isolation에는 격리 수준이 존재한다.
동시에 트랜잭션 작업이 이루어질 때, A 트랜잭션에서 B 트랜잭션의 조회중이거나 변경중인 데이터에 대한 접근을 허용할지 말지에 대한 수준을 결정하는 것이다.
Isolation Level에는 다음과 같이 네가지의 수준이 있다.
Level | Dirty-Read | Non-Repeatable Read |
Phantom-Read | |
1 | Read-Uncommited | O | O | O |
2 | Read-Committed | X | O | O |
3 | Repatable-Read | X | X | O |
4 | Serializable | X | X | X |
Read-Uncommited
- 다른 Transaction에서 업데이트가 커밋되지 않았더라도 변경된 데이터를 조회할 수 있다.
- A 트랜잭션에서 사용자 정보를 조회하는 도중, B 트랜잭션에서 사용자 정보가 변경되고 커밋되지 않았더라도 A에서는 커밋되지 않은 변경된 데이터를 조회하게 된다.
Read-Commited
- Commit되기 이전의 작업은 DB의 Undo 영역에 저장해 둔 후 Commit된 이후 실행되게 된다. (Dirty Read 방지)
- 트랜잭션이 시작되기 전에 커밋된 내용만 반영된 데이터에 대해서만 조회할 수 있는 격리 수준이다.
- 다른 Transaction에서 업데이트가 커밋되지 않았으면 변경된 데이터를 조회할 수 없다.
- 트랜잭션 도중 다른 트랜잭션에서 업데이트가 커밋되었다면 변경된 데이터를 조회할 수 있다.
- 커밋된 데이터만 조회할 수 있으나 동일한 쿼리에 대해서 다른 결과가 나올 수 있다.
- A 트랜잭션에서 여러번 사용자의 정보를 조회하고 있는 도중, 사용자의 정보를 변경하는 B 트랜잭션이 완료되어 Commit된다면, A에서는 같은 사용자 정보 조회 쿼리이지만 다른 결과가 나올 수 있다.
Repatable-Read
- 트랜잭션 내에서 조회하는 데이터에 대해 공유락을 걸어 잠금된 데이터의 변경 불가능이 보장된다.
- 공유락 : 리소스의 WRITE 제한
- 베타락 : 리소스의 READ/WRITE 제한
- 이를 위해서 DBMS의 트랜잭션은 트랜잭션 번호를 가지며, 자신보다 낮은 트랜잭션 번호에서 커밋된 내용만을 사용한다. 만약 Update와 같이 A 트랜잭션이 끝나기 이전에 실행되어서는 안되는 내용이 있다면 Undo 영역에 저장해 둔 후 A 트랜잭션이 끝난 이후 실행되게 된다.
- 관계형 데이터베이스에서는 대부분 해당 격리 수준을 기본으로 사용한다.
Update 부정합성
- 그러나 Update의 부정합성을 일으킬 수 있다. B 트랜잭션의 Update문은 A 트랜잭션이 완료되기 이전까지 Undo영역에 저장되게 된다. 하지만 A 트랜잭션 안에서도 Update문이 있다. 이렇다면 A의 업데이트가 실행되고 Commit된 이후 B의 업데이트가 실행되게 되어 결국 age는 21이 되는 부정합성이 일어나게 된다.
Phantom Read
- Repetable-Read는 트랜잭션 내에서 공유 잠금된 데이터의 Update 불가능이 보장된다. 그렇지만...
- A 트랜잭션에서 여러번의 조회 실행 중, B 트랜잭션 Insert이 된 후 테이블을 Select하면 없었던 B에서 Insert된 유령 데이터(Phantom Read)가 나타날 수 있다.
- Phantom Read : 트랜잭션 도중 다른 트랜잭션에서 Insert되거나 Delete가 발생하면, 트랜잭션 안에서 데이터가 추가되거나 삭제된 채로 조회되는 현상이 나타난다.
- 트랜잭션 도중 다른 트랜잭션에서 행 추가가 커밋되었다면 추가된 데이터를 조회할 수 있다.
- A 트랜잭션에서 여러번 사용자의 정보를 조회하고 있는 도중, 사용자를 추가하는 B 트랜잭션이 완료되어 Commit된다면, A에서는 같은 사용자 조회 쿼리이지만 추가된 행이 포함되어 다른 결과가 나올 수 있다.
Serializable
- 트랜잭션 내에서의 Select된 자원들은 공유 잠금된다.
- 트랜잭션 내에서 공유 잠금된 데이터의 수정 및 입력 불가능이 보장된다.
- Phantom Read가 발생하지 않는다.
- 가장 단순하면서 가장 엄격한 관리 수준이다.
- 성능 측면에서는 동시 처리 성능이 가장 낮다.
격리 수준의 선택
위에서 살펴보았을 때 그러면 무조건 격리 수준을 높이면 되는 것이 아닌가? 할 수도 있겠지만 격리 수준과 성능은 반비례하게된다.
따라서 일반적인 웹 어플리케이션의 비즈니스 로직들은 Read-Commited 수준으로 사용하는 경우가 많으나 금전적인 처리 등 예민한 데이터를 다룰 때에는 격리 수준을 높여야 한다.
아래와 같은 타임라인을 살펴보자
A | B | C의 잔액 |
A가 C에게 1000원 계좌이체 요청 | 0 | |
B가 C에게 2000원 계좌이체 요청 | 0 | |
C의 잔액 계산 : C의 잔액(0) + 1000 | 0 | |
... 처리중 | C의 잔액 계산 : C의 잔액(0) + 2000 | 0 |
송금 완료, C의 잔액 = C의 잔액(0) + 1000 | ... 처리중 | 1000 |
송금 완료, C의 잔액 = C의 잔액(0) + 2000 | 2000 |
A와 B가 C에게 송금을 할 때 격리수준이 낮으면 다음과 같은 현상이 발생하게 된다.
따라서 민감하고 엄격한 데이터일수록 Transaction의 격리 수준을 높이고, 그렇지 않은 일반적인 데이터는 격리 수준을 완화시키는 것이 좋다.
Reference
https://en.wikipedia.org/wiki/Isolation_(database_systems)
https://victorydntmd.tistory.com/129
'Database > 데이터베이스' 카테고리의 다른 글
[Database] 낙관적 락과 비관적 락 (0) | 2023.05.10 |
---|---|
[Database] 데이터베이스 정규화 (0) | 2023.02.04 |
[Database/Oracle] 데이터베이스 동의어 (0) | 2022.06.27 |
[Database/Oracle] 스키마 이름과 데이터베이스 링크 (0) | 2022.06.27 |