[Database] 트랜잭션의 격리 수준 (Isolation Level)에 대해 완벽하게 이해하기

2025. 3. 21. 17:52·Database

1. 트랜잭션과 데이터 정합성 문제

데이터베이스를 사용할 때 여러 개의 트랜잭션이 동시에 실행되면서 데이터 정합성 문제(Consistency Issue)가 발생할 수 있습니다.

트랜잭션(Transaction)은 데이터베이스에서 하나의 논리적인 작업 단위로, 반드시 모든 작업이 성공해야 완료(Commit)되고, 하나라도 실패하면 전체가 롤백(Rollback)되어야 합니다.

트랜잭션이 ACID 원칙을 준수해야 하는 이유도 바로 이러한 동시성 문제를 방지하기 위해서입니다.

그 중에서도 Isolation(격리수준)은 여러 트랜잭션이 동시에 실행될 때 서로 영향을 미치지 않도록 독립적으로 동작하는 것을 의미합니다.

만약 격리 수준을 적절히 설정하지 않으면, 동시성 문제(DIRTY READ, NON-REPEATABLE READ, PHANTOM READ)가 발생할 수 있습니다.

그렇다면, 트랜잭션의 격리 수준(Isolation Level)은 어떻게 동작하며, 어떤 문제가 발생할 수 있을까요?

2. Isolation Level (격리 수준)이란?

Isolation Level(격리 수준)이란 동시에 실행되는 여러 트랜잭션이 서로에게 미치는 영향을 조절하는 정도를 결정하는 것입니다.

데이터베이스에서는 여러 트랜잭션이 동시에 실행되며, 트랜잭션 간의 충돌을 방지하고 데이터 정합성을 유지하기 위해 격리 수준이 필요합니다.

트랜잭션이 수행될 때 다른 트랜잭션과 어느 정도까지 독립적으로 실행될 것인지(즉, 고립될 것인지)를 정의하는 것이 바로 격리 수준입니다.

격리 수준이 낮으면 동시성(성능)이 향상되지만, 데이터 정합성 문제(Dirty Read, Non-Repeatable Read 등)가 발생할 가능성이 높아집니다.

반대로 격리 수준이 높으면 데이터 정합성이 보장되지만, 트랜잭션이 순차적으로 실행되면서 성능이 저하될 수 있습니다.

즉, 격리 수준은 "성능 vs 데이터 정합성" 사이에서 균형을 맞추는 중요한 개념입니다.

2.1 왜 Isolation Level이 중요한가?

만약 격리 수준을 적절하게 설정하지 않으면 트랜잭션이 서로 영향을 미쳐 데이터 정합성이 깨질 가능성이 큽니다.

예를 들어, 한 트랜잭션이 데이터를 수정하는 동안 다른 트랜잭션이 해당 데이터를 읽거나 수정할 경우, 데이터가 예상과 다르게 동작할 수 있으며 심각한 데이터 무결성 문제를 초래할 수 있습니다.

  • 격리 수준이 낮으면?

→ 트랜잭션 간 동시 실행이 증가하여 성능은 향상되지만, 데이터 정합성 문제(DIRTY READ, NON-REPEATABLE READ, PHANTOM READ 등)가 발생할 가능성이 높아짐

  • 격리 수준이 높으면?

→ 트랜잭션 간 독립성이 증가하여 데이터 정합성은 보장되지만, 트랜잭션이 순차적으로 실행되면서 성능이 저하될 수 있음

따라서, 서비스의 특성과 요구사항에 맞는 적절한 격리 수준을 설정하는 것이 중요합니다.

2.2 Isolation Level (격리 수준) 종류

데이터베이스는 트랜잭션 간의 충돌을 방지하고 데이터 정합성을 보장하기 위해 4가지 격리 수준을 제공합니다.

격리 수준 설명 발생할 수 있는 문제
READ UNCOMMITTED 커밋되지 않은 데이터를 읽을 수 있음 Dirty Read, Non-Repeatable Read, Phantom Read
READ COMMITTED 커밋된 데이터만 읽을 수 있음 Non-Repeatable Read, Phantom Read
REPEATABLE READ 같은 트랜잭션 내에서 동일한 데이터를 조회하면 항상 같은 값이 보장됨 Phantom Read
SERIALIZABLE 트랜잭션을 순차적으로 실행 (가장 엄격한 수준) 없음 (가장 안전하지만 성능 저하)

1) 격리 수준 선택 기준

  • 격리 수준이 낮을수록 동시성(성능)은 증가하지만 데이터 정합성 문제 발생 가능성이 커짐
  • 격리 수준이 높을수록 데이터 정합성은 보장되지만 트랜잭션 처리 속도가 느려질 수 있음

→ 은행, 금융 시스템과 같이 데이터 정합성이 가장 중요한 서비스에서는 높은 격리 수준 (SERIALIZABLE) 사용

→ 쇼핑몰, 검색 엔진 등 동시성이 중요한 서비스에서는 낮은 격리 수준 (READ COMMITTED 또는 REPEATABLE READ) 사용

격리 수준을 올바르게 설정하면 데이터 정합성을 유지하면서도 성능을 최적화할 수 있습니다.

3. 동시성 문제 (Isolation Level에 따른 차이)

격리 수준이 낮을수록 트랜잭션 간의 동시 실행 성능은 향상되지만, 데이터 정합성 문제가 발생할 가능성이 커집니다.

반대로 격리 수준이 높아지면 데이터 정합성은 보장되지만 트랜잭션 처리 속도가 느려질 수 있습니다.

여기서는 각 격리 수준에서 발생할 수 있는 동시성 문제를 자세히 살펴보고, 이를 해결하는 방법을 정리해 보겠습니다.

3.1 Dirty Read (더티 리드)

커밋되지 않은 데이터를 다른 트랜잭션이 읽어버리는 문제

Dirty Read는 트랜잭션이 롤백될 경우, 읽은 데이터가 유효하지 않게 되는 심각한 데이터 정합성 문제를 일으킬 수 있습니다.

📌 Dirty Read 예제

-- T1: 트랜잭션 1이 값을 변경했지만 아직 커밋 안 함
T1: UPDATE users SET balance = 900 WHERE id = 1;

-- T2: 트랜잭션 2가 아직 커밋되지 않은 데이터를 읽어버림
T2: SELECT balance FROM users WHERE id = 1;  -- 900 (잘못된 값)

-- T1이 롤백하면?
T1: ROLLBACK;
  1. T1이 users 테이블에서 특정 사용자의 balance 값을 900으로 변경하지만, 아직 COMMIT하지 않음
  2. T2가 해당 balance 값을 읽어 900으로 인식
  3. T1이 ROLLBACK하면 데이터가 원래 값으로 복구되지만, T2는 잘못된 데이터를 읽어 사용하게 됨
  • 해결 방법
    • READ COMMITTED 이상의 격리 수준을 사용하면 Dirty Read를 방지할 수 있습니다.
    • READ COMMITTED는 커밋된 데이터만 읽을 수 있도록 보장하여 더티 리드를 방지합니다.

3.2 Non-Repeatable Read (반복 불가능한 읽기)

같은 데이터를 두 번 읽었을 때 값이 달라지는 문제

Non-Repeatable Read 문제는 트랜잭션이 진행되는 동안 동일한 데이터에 대한 조회 결과가 변할 수 있다는 점에서 문제가 됩니다.

예를 들어, 하나의 트랜잭션이 데이터를 읽고, 다른 트랜잭션이 해당 데이터를 수정한 후 커밋하면, 원래 트랜잭션이 같은 데이터를 다시 조회했을 때 값이 달라지는 문제가 발생할 수 있습니다.

📌 Non-Repeatable Read 예제


T1: SELECT balance FROM users WHERE id = 1;  -- 1000
T2: UPDATE users SET balance = 900 WHERE id = 1; COMMIT;
T1: SELECT balance FROM users WHERE id = 1;  -- 900 (값이 변함)
  1. T1이 특정 사용자의 balance 값을 읽음 (1000)
  2. T2가 해당 값을 900으로 변경 후 COMMIT
  3. T1이 다시 balance 값을 읽으면 900으로 변경됨 (값이 달라짐)
  • 해결 방법
    • REPEATABLE READ 이상의 격리 수준을 사용하면 Non-Repeatable Read 문제를 방지할 수 있습니다.
    • REPEATABLE READ는 트랜잭션이 시작된 시점의 데이터를 계속 유지하여, 같은 트랜잭션 내에서는 항상 동일한 데이터를 조회할 수 있도록 보장합니다.
    • 하지만 다른 트랜잭션이 새로운 데이터를 추가하는 것은 막지 못하므로, Phantom Read 문제는 여전히 발생할 수 있습니다.

3.3 Phantom Read (팬텀 리드)

같은 조건으로 조회했는데, 중간에 다른 트랜잭션이 데이터를 추가/삭제해서 결과가 달라지는 문제

Phantom Read는 트랜잭션이 실행되는 동안 WHERE 절의 조건에 맞는 행이 추가되거나 삭제될 때 발생합니다.

이 문제는 트랜잭션이 처음 실행될 때와 동일한 조건으로 데이터를 조회하더라도, 트랜잭션이 끝나기 전에 추가된 데이터가 나타나거나 삭제된 데이터가 사라지는 문제를 일으킵니다.

📌 Phantom Read 예제

T1: SELECT COUNT(*) FROM orders WHERE status = 'PENDING';  -- 10
T2: INSERT INTO orders (id, status) VALUES (101, 'PENDING'); COMMIT;
T1: SELECT COUNT(*) FROM orders WHERE status = 'PENDING';  -- 11 (데이터가 늘어남)
  1. T1이 status='PENDING'인 주문 개수를 조회 (10개)
  2. T2가 새로운 주문을 추가 (status='PENDING') 후 COMMIT
  3. T1이 다시 조회하면 개수가 늘어남 (11개)
  • 해결 방법
    • SERIALIZABLE 격리 수준을 사용하면 Phantom Read 문제를 방지할 수 있습니다.
    • SERIALIZABLE 수준에서는 모든 트랜잭션이 순차적으로 실행되므로, 트랜잭션이 진행 중인 동안 다른 트랜잭션이 데이터를 추가하거나 삭제할 수 없습니다.
    • 그러나, SERIALIZABLE은 동시성을 크게 제한하여 성능 저하를 초래할 수 있기 때문에 성능과 무결성 간의 균형을 맞춰야 합니다.

4. 동시성 문제 요약 (Dirty Read, Non-Repeatable Read, Phantom Read 비교)

문제 유형 설명 해결 가능한 격리 수준
Dirty Read 커밋되지 않은 데이터를 다른 트랜잭션이 읽어버리는 문제 READ COMMITTED 이상
Non-Repeatable Read 같은 데이터를 두 번 읽었을 때 값이 달라지는 문제 REPEATABLE READ 이상
Phantom Read 같은 조건으로 조회했는데, 중간에 다른 트랜잭션이 데이터를 추가/삭제하는 문제 SERIALIZABLE
  • 격리 수준이 낮을수록 동시성(성능)은 증가하지만, 데이터 정합성이 떨어질 위험이 커집니다.
  • 격리 수준이 높을수록 데이터 정합성은 보장되지만 성능이 저하될 수 있습니다.

5. 결론

  • 트랜잭션의 격리 수준(Isolation Level)은 동시성과 데이터 정합성 간의 균형을 맞추는 핵심 개념이다.
  • 격리 수준이 낮으면 성능은 좋지만 데이터 정합성 문제(DIRTY READ, NON-REPEATABLE READ 등)가 발생할 수 있다.
  • 격리 수준이 높으면 데이터 정합성은 보장되지만 성능이 저하될 수 있다.
  • 데이터 정합성이 중요한 경우(금융 서비스 등) 높은 격리 수준을 사용하고, 동시성이 중요한 경우 낮은 격리 수준을 선택할 수 있다.

트랜잭션을 설계할 때 어떤 데이터 정합성을 유지해야 하는지를 먼저 고민하고 격리 수준을 설정하는 것이 중요합니다.

'Database' 카테고리의 다른 글

[Database] SQL 성능 최적화: 빠른 쿼리를 위한 7가지 체크리스트  (1) 2025.03.05
[Database] 인덱스를 타지 않는 쿼리 10가지 경우 정리  (1) 2025.01.05
[Database] 데이터베이스 PK 전략 정리  (0) 2025.01.05
'Database' 카테고리의 다른 글
  • [Database] SQL 성능 최적화: 빠른 쿼리를 위한 7가지 체크리스트
  • [Database] 인덱스를 타지 않는 쿼리 10가지 경우 정리
  • [Database] 데이터베이스 PK 전략 정리
개발자 동긔
개발자 동긔
배우고 느낀점들을 기록합니다. 열정 넘치는 백엔드 개발자로 남고싶습니다.
  • 개발자 동긔
    Donker Dev
    개발자 동긔
  • 전체
    오늘
    어제
    • Category (39)
      • BackEnd (23)
        • JAVA (15)
        • Spring & JPA (7)
      • Database (4)
      • Computer Science (2)
        • Network (0)
        • Security (0)
        • Web (1)
      • DevOps (6)
        • Docker (1)
        • Jenkins (0)
        • Monitoring (2)
        • CICD (1)
      • 트러블 슈팅 (3)
      • 성능 개선 (1)
      • Project (0)
  • 인기 글

  • 태그

    CICD
    java
    @RequestBody
    restful api 설계
    master/slave db 이중화 처리
    docker compose
    Database
    인터페이스
    nginx
    spring boot
    JPA
    restful api
    mysql master/slave replication
    SSH
    docker
    Jenkins
    Spring
    interface
    spring cloud msa
    와일드카드
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
개발자 동긔
[Database] 트랜잭션의 격리 수준 (Isolation Level)에 대해 완벽하게 이해하기
상단으로

티스토리툴바