백엔드 개발자
DB 트랜잭션과 격리 수준 본문
1. 트랜잭션(Transaction)이란?
트랜잭션은 데이터베이스에서 수행되는 하나의 작업 단위로, 데이터베이스의 상태를 일관성 있게 유지하고, 여러 작업을 하나의 원자적 작업으로 묶어 처리할 수 있도록 한다. 즉, 트랜잭션 내에서 여러 쿼리가 실행되며, 그 결과가 모두 성공적으로 처리되거나, 아무것도 처리되지 않은 상태로 롤백되어야 한다.
하나의 논리적인 작업 단위라고 할 수 있다.
A가 B에게 돈을 송금한다.
라는 작업은 A의 계좌를 확인하고, 돈을 인출하고, B의 계좌를 확인하고, 돈을 더하는 작업으로 이루어질 수 있을 것이다.
실제 작업과 다를 수 있지만 간단하게 나타내면 이렇게 표현할 수 있을 것이다.
이런 트랜잭션은 다음과 같은 ACID 속성을 만족해야 한다:
- Atomicity (원자성): 트랜잭션은 모든 작업을 완전히 수행하거나, 하나도 수행하지 않은 것처럼 처리되어야 한다. 중간에 오류가 발생하면 트랜잭션은 이전 상태로 롤백되어야 한다.
- Consistency (일관성): 트랜잭션이 시작되기 전과 끝난 후의 데이터베이스 상태는 일관성 있는 상태여야 한다. 즉, 트랜잭션이 실행되기 전후에 데이터베이스의 규칙이나 제약 조건이 유지되어야 한다.
- Isolation (격리성): 동시에 실행되는 트랜잭션은 서로 독립적으로 실행되어야 하며, 다른 트랜잭션이 완료될 때까지 결과를 볼 수 없어야 한다. 하나의 트랜잭션이 완료되기 전에는 다른 트랜잭션의 변경이 반영되지 않아야 한다.
- Durability (지속성): 트랜잭션이 성공적으로 완료되면 그 결과는 영구적으로 저장되어야 하며, 시스템 장애가 발생하더라도 트랜잭션 결과는 보존되어야 한다.
2. ACID 속성에 따른 성능 문제
ACID 속성을 완벽하게 지키는 것은 데이터의 일관성과 안정성을 보장하지만, 성능에 부담을 줄 수 있다.
예를 들어 일관성과 독립성을 유지하기 위해 공유 자원에 동시에 접근하는 것에 대한 제어가 필요하다. 이런 제어가 엄격할수록, 동시 처리량은 줄어들기 때문에 ACID를 완벽하게 유지하는 것은 어느정도 포기하고 성능과의 균형을 맞출 수 있다.
이를 트랜잭션 격리 수준으로 나타낼 수 있다.
3. 트랜잭션 격리 수준 (Isolation Level)
트랜잭션의 격리성을 정의하는 방법으로 격리 수준을 설정한다. 격리 수준은 트랜잭션이 다른 트랜잭션의 변경을 얼마나 볼 수 있을지를 정의하며, 이 설정을 통해 성능과 동시성 처리 수준을 조절할 수 있다.
격리 수준의 종류 및 동작
- READ UNCOMMITTED:
- 동작: 다른 트랜잭션에서 커밋되지 않은 데이터를 읽을 수 있다. 이로 인해 더티 리드(Dirty Read)가 발생할 수 있다.
- 성능: 매우 빠르지만, 데이터의 일관성이 보장되지 않아 사용을 피하는 경우가 많다.
- 문제점: 데이터의 정확성이 보장되지 않으며, 트랜잭션이 롤백되면 읽은 데이터가 잘못된 데이터일 수 있다.
가장 낮은 수준의 격리 레벨이다. 가장 제약이 적기 때문에 빠른 속도를 낼 수 있지만, ACID를 보장할 수 없다. 가장 크게 나타내는 문제가 더티 리드인데, 커밋되지 않은 데이터를 읽은 경우 해당 트랜잭션이 롤백되면 데이터 일관성이 무너질 수 있다. 또 바로 DB에 반영시키기 때문에 롤백이 일어날 경우 이를 복구하는 작업이 필요하다
- READ COMMITTED:
- 동작: 트랜잭션이 데이터를 읽을 때, 다른 트랜잭션에서 커밋한 데이터만 읽을 수 있다. 더티 리드는 방지되지만, 비반복 읽기(Non-repeatable Read)가 발생할 수 있다.
- 성능: 성능과 일관성 간에 적절한 균형을 이루지만, 여전히 비반복 읽기 문제가 발생할 수 있다.
- 문제점: 트랜잭션 내에서 같은 데이터를 여러 번 읽을 경우, 다른 트랜잭션에 의해 값이 변경될 수 있다.
이 단계부터는 커밋된 데이터만 읽는데 이는 보통 MVCC를 활용해 구현할 수 있다. ACID의 I 독립성에서는 트랜잭션이 독립적으로 실행되는 것을 보장해야한다. 트랜잭션이 독립적으로 실행된다면 같은 데이터를 읽을 때 매번 같은 데이터를 보여줘야 할텐데, 이 수준에서는 반복해서 데이터를 읽을 때 다른 데이터가 보여질 수 있다.
- REPEATABLE READ:
- 동작: 트랜잭션이 데이터를 읽으면, 트랜잭션이 완료될 때까지 그 데이터를 다른 트랜잭션이 변경하지 못하도록 한다. 비반복 읽기는 방지된다.
- 성능: 성능이 떨어지며, 팬텀 리드(Phantom Read)라는 문제가 발생할 수 있다. 팬텀 리드는 다른 트랜잭션이 새 데이터를 삽입할 때 발생한다.
- 문제점: 새로운 데이터가 삽입되면 트랜잭션이 예기치 않게 영향을 받을 수 있다.
트랜잭션 내에서 같은 데이터에 대해 여러번 읽어도 같은 결과를 보장한다. 데이터를 읽을 때 공유락을 걸어 다른 트랜잭션이 해당 데이터를 수정할 수 없도록 한다.
- SERIALIZABLE:
- 동작: 가장 강력한 격리 수준으로, 트랜잭션들이 서로 직렬화되어 실행되는 것처럼 보인다. 즉, 다른 트랜잭션의 영향을 전혀 받지 않으며, 트랜잭션은 순차적으로 실행된다.
- 성능: 성능이 매우 낮아지며, 실제로 병렬 처리의 장점을 크게 잃게 된다.
- 문제점: 성능 저하가 극심하며, 많은 자원을 소모한다.
dirty read, 팬텀 리드 등 그 외에 발생할 수 있는 문제들이 모두 일어나지 않는다. ACID를 가장 엄격하게 보장하지만 그만큼 성능에서 손해를 볼 수 있다.
4. 동시성 제어 기법
동시성 제어는 여러 트랜잭션이 동시에 실행될 때 발생할 수 있는 충돌을 방지하기 위한 기법이다. 이때 중요한 기법은 MVCC와 락킹(Locking) 기법이 있다.
- MVCC (다중 버전 동시성 제어):
- 동작 원리: 각 트랜잭션마다 데이터의 버전을 관리하여, 한 트랜잭션이 데이터를 읽는 동안 다른 트랜잭션이 데이터를 수정할 수 없게 한다. 트랜잭션마다 자신만의 데이터 복사본을 사용하여, 격리성을 보장하면서 성능을 높인다.
- 장점: 성능과 일관성 사이에서 적절한 균형을 이룰 수 있다.
- 락킹(Locking):
- 동작 원리: 트랜잭션이 데이터를 변경할 때 다른 트랜잭션이 그 데이터를 변경할 수 없도록 잠금(Lock)을 걸어서 동시성 문제를 방지한다.
- 종류:
- 비관적 락(Pessimistic Locking): 트랜잭션이 데이터를 수정하는 동안 다른 트랜잭션이 해당 데이터에 접근하지 못하도록 잠금을 건다. 잠금을 해제하기 전까지 다른 트랜잭션은 그 데이터를 수정하거나 읽을 수 없다.
- 낙관적 락(Optimistic Locking): 트랜잭션이 데이터를 수정하기 전에 잠금을 걸지 않고, 수정 시점에 다른 트랜잭션이 해당 데이터를 수정했는지 확인하여 충돌 여부를 판별한다.
- 문제점: 락킹 기법은 데드락(Deadlock) 문제를 일으킬 수 있다.
'CS > 데이터베이스' 카테고리의 다른 글
SQL JOIN 종류 [ INNER JOIN, LEFT JOIN, RIGHT JOIN, OUTER JOIN] (1) | 2023.08.31 |
---|