트랜잭션과 동시성
하나의 앱에서 하나의 트랜잭션만 사용한다면 동시성에 대한 이슈는 없을 것이다.
아니면 여러 트랜잭션이 서로 다른 데이터만 사용한다면 동시성에 대한 이슈는 없을 것이다.
하지만 한명의 사용자를 위한 앱이 아닌 이상 현실적으로 그럴 수 없다.
때문에 트랜잭션 다룰 때는 동시성 이슈는 필연적으로 발생한다.
트랜잭션을 지원하는 데이터베이스들은 동시성 문제를 트랜잭션 격리 레벨로 해결해 왔다.
격리 레벨이라고 불리는 이유는 트랜잭션시 발생할 수 있는 다양한 동시성 문제를 단계별로 해결하기 때문이다.
트랜잭션 격리 레벨
SQL 표준은 Transaction Isolation에 대해 4가지 레벨로 정의하고 있다.
(위에서 아래로 격리레벨이 높아진다.)
- Read uncommitted(커밋 되지 않은 읽기)
- 커밋하지 않은 데이터를 읽을 수 있다.
- Read committed(커밋 후 읽기)
- 질의 시작 전에 커밋된 데이터만 읽을 수 있다.
- Repeatable read(반복 읽기)
- 트랜잭션 시작 전에 커밋된 데이터만 읽을 수 있다.
- Serializable(직렬성)
- 트랜잭션을 한 줄로 세워 차례대로 진행되는 것과 같은 결과를 보장한다.
트랜잭션 격리 레벨은 RDBMS마다 용어와 구현의 차이가 조금씩 있지만 맥락은 거의 비슷하다.
- Postgresql의 경우 가장 낮은 단계인 "Read uncommitted"을 구현하지 않고 명칭으로만 지원한다.
- Oracle의 경우 "Serializable" 단계를 지원하지만 구현된 스팩은 "Repeatable read(반복 읽기)"와 같다.
각 단계는 낮을 수록 더 많은 동시성 이슈를 허용한다.
- 종류: Dirty Read, Unrepeatable Read, Phantom Read, Serialization Anomaly
트랜잭션 격리레벨이 존재하는 이유는 뭘까?
그냥 가장 높고 좋아보이는 "직렬성" 격리레벨을 쓰면 안되는 걸까? 라는 생각을 할 수도 있다.
트랜잭션 격리레벨을 나눈데 이유는 (어김없이 등장하는)트레이드 오프 때문이다.
직렬성 격리레벨의 구현 방법은 심플하다 동시에 들어오는 여러 트랜잭션 요청을 큐처럼 하나씩 줄세워 처리하는 것이다.
여러 트랜잭션을 하나씩 처리하기 때문에 여러 데이터에 동시에 접근하여 생기는 동시성 문제는 생길 여지가 없다.
그리고 여러 요청이 하나씩 처리된다는 말은 그 만큼 지연이 발생하고 속도는 느려진 다는 것이다.
때문에 직렬성은 현실성이 없는 격리 레벨이라고 말하기도 하며, Oracle의 경우는 직렬성 격리를 구현조차 하지 않는다.
반면 가장 낮은 격리 레벨인 "Read uncommitted(커밋 되지 않은 읽기)" 경우는 격리 자체를 하지 않는 방법을 사용하기 때문에 더티읽기, 더티쓰기 같은 문제를 신경쓰지 않는다.
격리 레벨은 레벨마다 다양한 문제를 해결하고 동시에 성능적인 손실을 발생시킨다.
때문에 이를 사용하는 개발자는 격리 레벨이 해결하는 문제를 이해하고 도메인 맞춰 적절하게 사용하는 노력이 필요하다.
'{시리즈} > 트랜잭션' 카테고리의 다른 글
3. 트랜잭션 사용 방법과 어보트 처리 (0) | 2023.08.29 |
---|---|
0. 트랜잭션 시리즈 작성 목적 (0) | 2023.08.22 |
2. 트랜잭션에서 안정성을 의미하는 ACID (0) | 2023.08.21 |