우당탕탕
[MySQL] 트랜잭션 격리 수준(Isolation Level)에 대한 이해 본문
트랜잭션 격리수준에 대한 이해
트랜잭션 격리수준(Isolation Level)
트랜잭션 격리수준은 여러 트랜잭션이 동시에 실행될 때 서로의 영향을 얼마나 차단할지를 결정하는 기준이다. SQL 표준에서는 네 가지 격리수준을 정의하고 있으며, 각 수준은 성능과 데이터 무결성 간의 균형을 다르게 설정한다.
이번 글에서는 네가지 격리수준에 대한 설명과 예시 코드를 알아보자
Read Uncommitted (읽기 미확정)
가장 낮은 격리수준인 Read Uncommitted는 다른 트랜잭션이 커밋하지 않은 데이터를 읽을 수 있게 하는 설정이며, 이로 인해 "더티 리드(Dirty Read)"가 발생할 수 있으며, 이는 비즈니스 로직에 심각한 영향을 미칠 수 있는 단점이 있다.
👩💻 더티 리드(Dirty Read)란?
- 더티 리드는 한 트랜잭션이 다른 트랜잭션에서 아직 커밋되지 않은 변경사항을 읽는 상황을 말한다. 이 경우에 읽은 데이터가 롤백되면 일관성이 깨지게 된다. 즉, 잘못된 데이터를 기반으로 작업을 수행할 위험이 존재하는 것이다.
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN TRANSACTION;
SELECT * FROM Orders; -- 아직 커밋되지 않은 데이터도 읽을 수 있음
COMMIT TRANSACTION;
Read Committed (읽기 확정)
Read Committed는 커밋된 데이터만 읽을 수 있도록 하여 Read Uncommitted에서 발생하던 더티 리드를 방지한다. 하지만 "비 반복적 읽기(Non-Repeatable Read)" 문제가 여전히 발생할 수 있다.
👩💻 비 반복적 읽기(Non-Repeatable Read)란?
- 비 반복적 읽기는 동일한 트랜잭션 내에서 같은 데이터를 두 번 읽을 때, 두 번의 결과가 서로 다른 경우를 말한다. 이는 다른 트랜잭션이 데이터를 변경했기 때문에 발생한다.
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN TRANSACTION;
SELECT * FROM Orders WHERE OrderID = 1; -- 첫 번째 읽기
-- 다른 트랜잭션에서 OrderID = 1의 상태가 변경될 수 있음
SELECT * FROM Orders WHERE OrderID = 1; -- 두 번째 읽기, 결과가 다를 수 있음
COMMIT TRANSACTION;
Repeatable Read (반복 가능 읽기)
Repeatable Read는 동일한 트랜잭션 내에서 동일한 데이터를 반복해서 읽을 때 항상 같은 결과를 보장한다. Read Committed와 다르게 비 반복적 읽기 문제는 해결되지만, "팬텀 리드(Phantom Read)" 문제는 여전히 남아 있다.
👩💻 팬텀 리드(Phantom Read)란?
- 팬텀 리드는 트랜잭션이 실행되는 동안 다른 트랜잭션이 새로운 레코드를 추가하거나 삭제하여, 처음 읽었던 결과와 두 번째 읽었을 때의 결과가 서로 다른 경우를 말한다. 이 상황은 반복 가능 읽기에서도 발생할 수 있다.
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRANSACTION;
SELECT * FROM Orders WHERE CustomerID = 'C001'; -- 첫 번째 읽기
-- 다른 트랜잭션에서 새로운 주문을 추가할 수 있음
SELECT * FROM Orders WHERE CustomerID = 'C001'; -- 두 번째 읽기, 결과는 동일함
COMMIT TRANSACTION;
Serializable (직렬화 가능)
가장 높은 격리수준인 Serializable은 모든 트랜잭션이 순차적으로 실행되는 것처럼 동작하여, 더티 리드, 비 반복적 읽기, 팬텀 리드를 모두 방지한다.
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
SELECT * FROM Orders WHERE OrderID < 10; -- 첫 번째 읽기
-- 다른 트랜잭션에서 OrderID가 10 이하인 주문을 추가할 수 없음
SELECT * FROM Orders WHERE OrderID < 10; -- 두 번째 읽기, 결과는 동일함
COMMIT TRANSACTION;
트랜잭션 격리수준의 선택 기준
트랜잭션 격리수준을 선택할 때는 애플리케이션의 특성과 기능에 따라 고려해야한다. 예를 들어, 실시간 데이터 처리가 중요한 경우 Read Uncommitted나 Read Committed가 적합할 수 있지만, 데이터의 일관성이 매우 중요한 경우에는 Serializable을 선택해야 한다.
실제 적용 사례
많은 기업들이 트랜잭션 격리수준을 조정하여 성능을 최적화하고 있다. 예를 들어, 전자상거래 플랫폼에서는 Read Committed를 사용하여 사용자 경험을 극대화하면서도 데이터의 무결성을 하는 반면, 은행 시스템에서는 Serializable을 사용하여 모든 거래의 정확성을 확보한다.
예시코드( 전자상거래 플랫폼 )
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN TRANSACTION;
-- 재고 확인
SELECT Stock FROM Products WHERE ProductID = 'P001';
-- 주문 처리
INSERT INTO Orders (ProductID, Quantity) VALUES ('P001', 1);
COMMIT TRANSACTION;
예시코드 ( 은행 )
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
-- 잔액 확인
SELECT Balance FROM Accounts WHERE AccountID = 'A001';
-- 출금 처리
UPDATE Accounts SET Balance = Balance - 100 WHERE AccountID = 'A001';
COMMIT TRANSACTION;
각 트랜잭션 격리수준은 성능과 데이터 무결성 간의 균형을 다르게 설정하므로, 애플리케이션의 요구사항에 따라 적절한 격리수준을 선택하는 것이 중요하다.