[DB] DB 트랜잭션
Intro
원티드 백엔드 챌린지를 수행하면서 배웠던 내용과 기존의 개념들을 다시 잡아보려 한다.
트랜잭션(Transaction)
트랜잭션(Transaction), 데이터베이스의 상태를 변화시키기 위해 수행되는 작업의 단위를 의미한다.
트랜잭션(Transaction) 특징 - ACID
ACID는 데이터의 무결성과 안정성을 보장해준다.
- 원자성(Atomcity) : 트랜잭션 작업이 모두 처리되거나 취소되어야 하는 성질이다.
- 송금 시스템에서 10,000원을 송금했을 때 보낸 사람 계좌에서 10,000이 차감되고, 받는 사람의 계좌에서 10,000이 증가되야 한다.
- 만약, 보낸 사람의 계좌만 차감되고 받는 사람의 계좌는 증가되지 않는다면 엄청난 문제를 발생시킨다.
- 그렇기에 보낸 작업, 받는 작업 모두를 취소하고 새로운 송금(트랜잭션)을 해야 한다. 이를 ROLLBACK이라 한다.
- 원자성이란 트랜잭션 작업이 일부분만 처리되는 것을 방지해주는 성질이라고 생각하면 된다.
- 송금 시스템에서 10,000원을 송금했을 때 보낸 사람 계좌에서 10,000이 차감되고, 받는 사람의 계좌에서 10,000이 증가되야 한다.
- 일관성(Consistency) : 데이터베이스의 작업이 완료되면 일관적인 DB 상태를 유지하는 성질이다.
- 예를 들어, A와 B가 서로에게 각각 다른 금액을 송금한다고 가정하자.
- 서로 송금을 할 때 각자의 잔고를 확인하고 송금을 해야 한다.
- 누군가 확인하지 않고 보내게 되면 일관성이 무너지게 된다. 그렇기에 제약 및 규칙을 따라야 한다.
- 격리성(Isolation) : 트랜잭션은 독립적으로 수행되야 하는 성질이다.
- 즉, 격리성은 한 작업이 진행되고 있을 때 다른 작업이 끼어들지 못하는 성질이다.
- 예를 들어, 송금할 당시 10,000원이 잔고로 있어서 10,000을 송금하는데 동시에 자동 인출(통신비)된다고 가정하자.
이 작업이 동시에 진행됨에 따라 잔고가 마이너스가 되는 현상이 발생한다. 그렇기에 격리성을 보장해야 한다. - 격리수준을 설정하여 위와 같은 문제를 해결할 수 있다.(격리 수준은 하단에서 설명합니다.)
- 즉, 격리성은 한 작업이 진행되고 있을 때 다른 작업이 끼어들지 못하는 성질이다.
- 지속성(Durability) : commit이 되면 트랜잭션은 DB에 영구적으로 반영되는 성질.
- 데이터베이스를 안전하게 보장하는 성질로, 예기치 않는 상황에서도 데이터의 영속성을 보장하는 성질이다.
- 계좌 이체를 성공적으로 마쳤으면 DB오류로 인해 종료되더라도 로그를 기록하여 이후 상황을 해결할 수 있다.
격리 수준(Isolation Level)
격리 수준이란, 트랜잭션끼리 얼마나 서로 고립되어 있는지를 나타내는 수준이다.
즉, 한 트랜잭션이 다른 트랜잭션이 변경한 데이터에 대한 접근 강도를 의미한다.
격리 수준이 높아질수록 데이터의 정합성과 동시성을 증가시킨다.
반대로, 격리 수준이 낮아질 수록 데이터의 정합성과 동시성을 감소시킨다.
격리 수준에는 READ UNCOMMITTED, READ COMMITTED, REPETABLE READ, SERAIALIZABLE이 있다.
그 중 일반적으로는 READ COMMITTED, REPETABLE READ 중 하나를 사용한다.
낮은 수준을 설정했을 때 3가지 현상이 발생한다. (Dirty Read, Non-repeatable Read, Phantom Read)
Dirty Read란, 커밋되지 않은 다른 트랜잭션의 데이터를 읽는 것을 의미한다.
Non-repeatable Read란, 한 트랜잭션 안에서 같은 쿼리를 두 번 실행했을 때, 다른 값이 나오는 Read현상을 의미한다. (하나의 트랜잭션에서 여러 스냅샷을 사용되는 경우)
Phantom Read란, 다른 트랜잭션이 커밋한 데이터가 있더라도 자신의 트랜잭션에서 읽었던 내용만 사용하는 것을 의미한다.
Read Uncommitted
한 트랜잭션 작업이 commit 되지 않아도 읽을 수 있는 수준
커밋되지 않은 데이터를 읽어오기에 Read Uncommitted를 설정하게 되면 Dirty Read, Non-repeatable Read, Phantom Read가 발생할 수 있다. (권장하지 않는 수준)
Read Committed
commit 된 작업(변경된 작업)만 읽을 수 있는 수준
Read Committed는 데이터가 변경되면 undo 영역에 변경 전 데이터를 보관한다.
커밋 전까지는 undo영역에 있는 데이터를 읽고, 커밋이 되면 변경된 작업을 읽는다.
Read Committed에서는 Non-Repeatable Read 문제가 발생한다.

A 트랜잭션에서 처음 조회했을 때 10,000원이 있었는데 중간에 B 트랜잭션이 1,000원을 송금하고 커밋을 했다고 가정하자. 이 때 B 트랜잭션이 커밋을 했기에 A 트랜잭션에서 커밋 시점 이후로 조회를 하면 첫 조회 결과와 다른 11,000원이 조회된다.
A 트랜잭션에서 동일한 트랜잭션에서 동일한 조회를 했음에도 다른 결과가 나온다는 문제점이 있다.
Repeatable Read
한 트랜잭션 작업이 종료될 때 까지 동일한 값을 반환하는 수준. (동일한 트랜잭션 내에서 동일한 값을 반환한다.)
Repeatable Read 수준은 트랜잭션마다 트랜잭션ID를 저장한다.(커밋될 때 마다 id는 증가되어 부여된다.)
Repeatable Read 수준은 자신의 트랜잭션 ID보다 동일하거나 작은 트랜잭션에서 변경한 내용만 확인이 가능하다.
그렇기에 Repeatable Read는 종료될 때까지 동일한 값을 반환한다.(변경된 내용은 트랜잭션 ID가 높기에 조회가 안된다.)
예를 들어, 잔고가 10,000이 있을 때 송금을 1,000원을 했다고 가정하자. 원래는 조회했을 때 9,000원이 나와야 하지만 10,000이 조회된다. 또한 누군가 송금을 하더라도 금액이 추가되지 않는다.
Serialiable
모든 트랜잭션 작업이 독립적으로 실행되는 수준.
Seralizable 수준에서는 모든 작업이 독립적으로 수행되면 격리성 보장이 확실하게 된다.
또한 앞에서 설명한 Dirty Read, Non-repeatable Read, Phantom Read가 모두 해결된다.
하지만, 서비스 속도가 현저하게 저하될 것이다.
정리
