트랜잭션의 관리 수준

viroovr·2024년 10월 15일
post-thumbnail

트랜잭션 관리 수준,, 이게 뭔 말일까
트랜잭션은 ACID 특성을 가진 데이터베이스의 주요 작동 요소이다.
이를 관리하는 수준이 과연 뭘까.
한번 알아보자

트랜잭션 관리 수준은 데이터베이스에서 동시에 여러 트랜잭션이 실행될 때 발생할 수 있는 데이터 불일치 문제를 방지하기 위해 설정하는 격리 수준이다. 각 트랜잭션이 다른 트랜잭션의 영향을 얼마나 받을지를 정의하며, 네 가지 주요 격리 수준이 있다.

아하. 동시에 여러 트랜잭션이 일어나면 Consistency, Isolation 이 위배될 수 있다.
이것을 방지하기 위해 개발자는 트랜잭션의 격리 수준을 설정해야 한다.

  1. Read Uncommitted: 트랜잭션이 아직 커밋되지 않은 데이터를 다른 트랜잭션에서 읽을 수 있는 수준이다. 가장 낮은 격리 수준으로, Dirty Read, Non-Repeatable Read, Phantom Read가 모두 발생할 수 있다.
  2. Read Committed: 트랜잭션이 커밋된 데이터만 읽을 수 있는 수준이다. Dirty Read는 방지되지만, Non-repeatable Read와 Phantom Read는 여전히 발생할 수 있다.
  3. Repeatable Read: 트랜잭션이 시작될 때의 데이터를 지속적으로 읽을 수 있는 수준으로, 트랜잭션 동안 같은 데이터를 여러 번 읽어도 동일한 결과를 보장한다. Dirty Read와 Non-repeatable Read는 방지되지만, Phantom Read는 여전히 발생할 수 있다.
  4. Serializable: 가장 높은 격리 수준으로, 트랜잭션 간의 완벽한 격리를 보장한다. Dirty Read, Non-repeatable Read, Phantom Read모두 방지된다. 하지만 성능 저하가 발생할 수 있다.

이러한 네가지 격리 수준이 존재한다. 격리 수준이 올라갈 수록 데이터 일치는 보장되지만 그만큼 성능이 저하되는 트레이드 오프 관계에 놓인다.

솔직히 읽기만 하면 와닿지가 않는다. 격리수준을 직접 실습하면서 알아보자.

환경 설정

  • 두 개의 MySQL 클라이언트 세션을 사용하여 실습을 진행한다.
  • 각 세션은 하나의 트랜잭션을 시작하고, 격리수준을 다르게 설정하여 동일한 데이터를 읽고 수행하는 실습을 진행한다.

1. Read Uncommitted

Read Uncommitted 수준에서는 트랜잭션이 커밋되지 않은 데이터를 다른 트랜잭션에서 읽을 수 있다. 이로 인해 Dirty Read가 발생할 수 있다.

  • 세션 1
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
UPDATE accounts SET balance = 5000 WHERE id = 1;
  • 세션 2
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
SELECT balance FROM account WHERE id = 1;

여기서 세션 2는 커밋되지 않은 balance 5000값을 읽을 수 있다.

  • 세션 1
ROLLBACK;

세션 1이 롤백한다면, 세션 2는 비정상적인 데이터를 읽은 셈이 된다.
이를 통해 트랜잭션이 커밋되지 않더라도 다른 트랜잭션에서 해당 데이터를 읽을 수 있는 Dirty Read문제를 확인할 수 있다.

2. Read Committed

Read Committed 수준에서는 트랜잭션이 커밋된 데이터만 읽을 수 있다. 이로 인해 Dirty Read는 방지되지만, Non-repeatable Read가 발생할 수 있다.

  • 세션1
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
UPDATE accounts SET balance = 7000 WHERE id = 1;
  • 세션2
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
SELECT balance FROM accounts WHERE id = 1;

여기서 세션 2는 기존 값을 읽게된다.

  • 세션1
COMMIT;
  • 세션2
SELECT balance FROM accounts WHERE id = 1;

변경된 balance 7000값을 읽어오게 된다.
세션 2가 두번의 SELECT에서 다른 값을 읽는 현상을 통해 Non-repeatable Read문제를 확인할 수 있다.
곧, 트랜잭션 내에서 데이터를 여러 번 읽을 때, 그 값이 항상 동일해야 하는 기대를 져버리게 된다.
데이터 무결성이 손상되고 비즈니스 로직에 혼란이 발생할 문제가 있다.

3. Repeatable Read

Reapeatable Read수준에서는 트랜잭션이 시작될 때 읽은 데이터가 이후 트랜잭션이 끝날 때까지 동일하게 유지된다. 하지만 Phantom Read는 여전히 발생할 수 있다.

  • 세션1
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
SELECT balance FROM account WHERE id = 1;
-- 5000을 읽어온다.
  • 세션2
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
UPDATE accounts SET balance = 6000 WHERE id = 1;
COMMIT;
  • 세션1
SELECT balance FROM accounts WHERE id = 1;
COMMIT;

세션 2가 값을 수정했음에도 불구하고, 여전히 5000을 읽어온다. Non-Repeatable Read가 방지된다.
세션 1이 트랜잭션 내에서 동일한 값을 계속해서 읽을 수 있는 것을 확인할 수 있다.
하지만 중간에 Insert가 일어난다고 할때, 트랜잭션이 처음 데이터를 조회했을 때와 나중에 같은 쿼리를 실행 했을 때, 행의 개수가 달라지는 문제가 발생할 수 있다.
즉, 트랜잭션 내에서 동일한 조건으로 여러 번 데이터를 읽을 때, 중간에 다른 트랜잭션이 새로운 행을 삽입하거나 삭제함으로써 결과가 달라지는 Phantom Read 현상이 발생할 수 있다.

단순히 수정된 데이터 조회의 결괏값이 달라지는 문제뿐만 아니라 삭제, 삽입에 대해서도 일관성을 기대하는 것이다.

4. Serializable

Serializable 수준에서는 트랜잭션이 완전히 격리되어 실행되므로 Dirty Read, Non-repeatable Read, Phantom Read 모두 방지된다.

  • 세션1
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;
SELECT balance FROM account WHERE id = 1;
-- 5000을 읽어온다.
  • 세션2
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;
UPDATE accounts SET balance = 6000 WHERE id = 1;

세션 1이 끝나기 전까지 대기해야 한다.

  • 세션1
COMMIT;
  • 세션2
COMMIT;

실습을 통해 어떤 식으로 격리수준이 관리되며, 발생할 수 있는 문제를 중심으로 살펴봤다.
앞으로 트랜잭션 격리수준을 접하게 되면, 왜 해당 트랜잭션 격리수준을 선택했는지, 성능, 일관성과의 트레이드 오프를 어떤 식으로 처리했는지를 살펴볼 수 있겠다.

profile
성장하는 개발자

0개의 댓글