트랜잭션(Transaction)의 격리 수준과 동시성

zayn_noh·2026년 6월 10일

DB(DataBase)

목록 보기
2/3
post-thumbnail

데이터베이스에서 트랜잭션(Transaction)은 데이터 정합성을 지키기 위한 핵심 개념이다.

서비스 로직은 보통 여러 개의 SQL 작업으로 이루어지지만,
비즈니스 관점에서는 이 작업들이 하나의 단위로 함께 성공하거나 함께 실패해야 하는 경우가 많다.

이때 트랜잭션은 여러 작업을 하나의 논리적 단위로 묶어 데이터가 잘못된 상태로 남지 않도록 보장한다.
그리고 이러한 트랜잭션의 안정성을 설명하는 대표적인 특성이 ACID이다.

이번 포스트에서는 트랜잭션의 기본 개념과 상태, ACID 속성을 먼저 정리하고

그중 동시성 문제와 가장 밀접한 Isolation을 중심으로,
Isolation Level트랜잭션 이상 현상이 어떤 관계를 가지는지 살펴본다.


1. 트랜잭션(Transaction)과 ACID 속성

1-1) 트랜잭션의 정의

트랜잭션(Transaction)이란 비즈니스 로직, 정책 특성상 나누어지면 안되는 하나의 작업 단위를 말한다.

이를 데이터베이스에서의 관점에서 바라보면, DB와의 상호작용 단위이자, 일련의 트랜잭션 과정 속에서
특정 데이터를 다룰 때 절대 나뉘어져서는 안되는 논리적인 작업의 최소 단위라 할 수 있다.

예시를 통해 이를 이해하면,
어떤 사용자가 상품을 구매하는 일련의 과정을 하나의 트랜잭션으로 다룰 수 있다.

  • 상품 재고 확인
  • 사용자 통장에서 잔액 차감 통장 잔액 - 상품 가격
  • 상품 재고 감소 -1
  • 판매자 통장에 잔액 추가 통장 잔액 + 상품 가격

만약 위 과정에서 사용자 통장의 잔액이 빠져나갔지만, 이후 과정에서 에러가 발생한다면
사용자는 주문을 했음에도 상품을 받지 못하고 통장에서 잔액만 빠져나갈 수 있다.

이러한 일련의 과정은 모두 동시에 성공하거나,
하나라도 실패하면 전체 과정을 없던 일로 해야 올바른 서비스 로직이 될 수 있다.

이에 트랜잭션(Transaction)이라는 하나의 흐름으로 작업의 단위를 정의하고 다룰 수 있다.

트랜잭션은 여러 SQL 쿼리를 하나의 논리적 묶음으로 만들어 전부 성공하거나 전부 실패를 보장한다.


1-2) 트랜잭션의 상태

트랜잭션은 로직 처리 과정에서 각각 다음과 같은 상태를 가질 수 있다.

  • Active
    트랜잭션이 시작되어 SQL 작업을 수행 중인 Active 상태(활성 상태)
    이는 트랜잭션의 시작점이자 지금부터 시작하는 SQL 쿼리들은 동일 단위 트랜잭션에 포함됨을 의미한다.
  • Partially Committed
    단어의 뜻은 부분적 완료 상태를 의미하며, SQL 모든 작업이 성공적으로 마지막까지 실행된 상태이다.
    모든 작업이 끝났기에 마지막 확정(Committed)만을 기다린다.
  • Committed
    Commit 명령이 실행되면 트랜잭션은 Committed, 완료 상태가 된다.
    이후 수정되거나 추가, 삭제된 데이터가 DB에 영구적으로 저장되고 지속성을 갖는 데이터가 된다.

  • Failed
    트랜잭션 수행 중 오류나 장애가 발생해 더 이상 정상적으로 진행할 수 없는 상태이다.
    이후 변경사항을 되돌리기 위해 rollback 과정으로 넘어간다.
    오류의 종류DBMS(PostgreSQL MySQL etc.)에 따라 트랜잭션 전체가 실패 처리되는 지,
    해당 SQL만 실패하고 트랜잭션은 계속 가능한지 처리가 다를 수 있다.

  • Aborted
    Failed 상태에서 ROLLBACK이 수행되어 트랜잭션 시작 이전 상태로 데이터가 되돌아간 상태이다.
    즉, 실패한 트랜잭션의 변경사항이 모두 철회된 상태를 의미한다.

  • Terminated
    트랜잭션 생명주기의 최종 종료 상태
    트랜잭션이 commit 또는 rollback 이후 완전히 종료된 상태를 말한다.

왜 Partial Committed와 Committed 상태가 나뉘어 있나?

트랜잭션 내부 모든 SQL 작업이 성공적으로 실행되었다고 하여
그 결과가 즉시 영구적으로 확정되는 것은 아니다.
모든 작업이 끝난 뒤에도 commit 로그 기록, 디스크 반영, 장애 복구를 위한 정보 저장 같은
커밋 처리가 성공해야 비로소 트랜잭션은 Committed 상태가 된다.

커밋 처리 과정에서 오류가 발생하면 트랜잭션은 Failed 상태로 전이될 수 있다.
따라서 Partially Committed 상태는
“작업은 모두 끝났지만 아직 최종 확정은 되지 않은 상태”를 구분하기 위해 존재한다.

트랜잭션은 단순히 여러 SQL 구문을 하나로 묶는 것이라 보면 안된다.
비즈니스적 관점에서 "해당 작업은 모두 동시에 성공하거나, 실패하여야 한다"라고 보는 작업의 단위를
데이터베이스 속 데이터의 관점에서 지키는 장치 역할을 하는 것이라 생각해야 한다.


1-3) 트랜잭션의 범위 설정

트랜잭션은 보통 개발자가 비즈니스 규칙, 논리에 따라서 어디까지 묶고 처리할지를 결정한다.

이를 트랜잭션의 범위를 결정한다고 하며
이러한 범위는 언제나, 가능한 최소 범위로 잡고 SQL 작업을 처리해야 한다.

데이터베이스의 커넥션 풀(Connection Pool)

백엔드 서버 어플리케이션은 DB와 통신을 위해 커넥션(Connection)을 사용한다.

하지만 모든 스레드에서 DB 데이터 관련 요청이 들어올 때마다, DB 커넥션을 새로 생성하고 종료한다면
비용이 크기 때문에 보통 미리 일정 개수의 커넥션을 만들어두고 재사용한다.
이를 커넥션 풀(Connetion Pool)이라 한다.

트랜잭션이 시작되면 일반적으로 하나의 DB 커넥션을 점유한 상태로 작업이 진행된다.
특히 내부에서 외부 API 호출, 파일 처리, 긴 연산, 사용자 응답 대기 같은 작업을 수행하면
실질적으로 DB 작업을 하지 않는 동안에도 커넥션을 점유하게 된다.
이는 커넥션 풀 고갈, 요청 지연, 전체 처리량 감소와 같은 성능 문제로 이어질 수 있다.

따라서 트랜잭션은 데이터 정합성을 보장해야 하는 DB 작업만 묶고,
가능한 짧은 범위로 설정하는 것이 좋다.


1-4) 트랜잭션이 가지는 특성 : ACID

트랜잭션은 데이터의 무결성일관성을 보장하고,
여러 작업을 하나의 논리적인 작업 단위로 묶어서 처리한다. ACID는 이러한 트랜잭션의 네 가지 특성을 말한다.

  • Atomicity(원자성)
    All or Nothing이라 할 수 있는 트랜잭션의 대표적인 특성을 말한다.
    트랜잭션에 포함된 작업은 전부 성공하거나, 단 하나도 실행되지 않은 것처럼 모두 취소되어야 하며
    부분적으로 성공하는 상태는 절대 허용하지 않는다.

  • Consistency(일관성)
    트랜잭션이 성공적으로 끝난 이후 데이터베이스는 일관된 상태를 유지해야한다는 특성이다.
    이때 일관성이란 특정 데이터 몇 개만이 아니라,
    DB에 정의된 제약조건과 비즈니스 규칙을 포함하여 유효한 상태를 말한다.

    예를 들어 사용자의 이름에는 숫자가 들어갈 수 없다는 규칙이 있다고 가정하자.
    이 규칙이 DB의 CHECK 제약조건이나 애플리케이션 로직으로 보장되고 있다면,
    트랜잭션 처리 결과로 특정 사용자의 이름에 숫자가 들어가는 것은 일관성을 깨는 결과가 된다.
    따라서 이러한 변경은 제약조건 위반으로 실패하거나,
    애플리케이션 검증 단계에서 차단되어 트랜잭션이 롤백되어야 한다.
  • Isolation(격리성)
    동시에 여러 트랜잭션이 실행될 때,
    각 트랜잭션은 다른 트랜잭션의 작업으로 인해 영향을 받지 않아야 한다는 특성이다.
    즉, 여러 트랜잭션이 동시에 실행되더라도
    각각의 트랜잭션은 독립적으로 실행되는 것처럼 보장되어야 한다.

    이러한 격리성 문제는 동시성 문제와도 밀접하게 관련되어 있는데, 격리성이 충분히 보장되지 않으면

    • 하나의 트랜잭션이 아직 커밋되지 않은 데이터를 읽거나
    • 트랜잭션 내에서 동일 데이터를 다시 조회했을 때 값이 달라지거나
    • 기존에 없던 row가 새로 나타나는 등 이상 현상이 발생할 수 있다.

데이터베이스는 이러한 문제(이상 현상)를 제어하기 위해
트랜잭션의 격리 수준(Isolation Level)을 옵션으로 제공한다.

  • Durability(지속성)
    트랜잭션이 성공적으로 커밋되었다면, 이후 시스템 장애가 발생하더라도
    해당 변경사항은 데이터베이스에 영구적으로 보존되어야 한다는 특성이다.

    즉, 커밋된 데이터는 서버가 종료되거나 장애가 발생하더라도 사라지지 않아야 한다.

    데이터베이스는 이를 보장하기 위해 변경 내용을 디스크에 바로 반영하기 전,
    먼저 로그 파일에 기록하는 방식으로 복구 정보를 남긴다.
    장애가 발생하더라도 DBMS는 이 로그를 기반으로 커밋된 트랜잭션의 변경사항을 복구할 수 있다.



2. Isolation Level과 트랜잭션 이상 현상

앞서 살펴보았던 ACID 중 I, Isolation(격리성)을 보장하기 위해,
데이터베이스에서 제공하는 네 가지 격리 수준(Isolation) 옵션들에 대해서 알아보도록 하겠다.

이때 격리 수준이란 여러 트랜잭션이 동시에 실행될 때,
서로 다른 작업 내용을 어디까지 보지 못하게 할 것인지를 정한 기준이라 생각하면 된다.
“트랜잭션 간 데이터 간섭을 얼마나 허용할 것인가"

무조건 격리 수준이 높은게 좋은 것 아닐까

트랜잭션의 격리 수준이 높으면 여러 트랜잭션이 동시에 실행될 때
발생할 수 있는 이상 현상을 더 강하게 막을 수 있다. 하지만

격리 수준이 높다면 동시성과 연관되는 문제로 이어져 성능적 문제로 이어질 수 있다.

격리 수준이 높은 경우
동시성 하락, 락 유지 시간 증가, 다른 트랜잭션의 대기 시간 증가, 처리 성능 감소

트랜잭션 이상 현상

Dirty Read
두 개의 트랜잭션이 있을 때, 다른 트랜잭션이 수정한 아직 커밋하지 않은 데이터를 읽는 현상

다른 트랜잭션이 수정한 데이터를 읽었는데,
해당 트랜잭션이 롤백되어 잘못된 데이터를 사용하는 경우 문제 발생

Non-Repeatable Read
하나의 트랜잭션 안에서 같은 데이터를 여러 번 조회했는데, 각 조회 결과가 다르게 나오는 현상

Phantom Read
하나의 트랜잭션 안에서 특정 범위의 데이터를 여러 번 조회했는데
첫 조회에서 없었던 유령같은 데이터가 나타나는 현상

1. Read Uncommitted

사실상 격리성을 보장해주지 않는 옵션에 해당한다.
트랜잭션 중 한 트랜잭션이 아직 커밋하지 않은 값을, 다른 트랜잭션이 그대로 조회하는 것을 허용한다.

데이터의 정합성이 쉽게 깨질 수 있어 거의 사용되지 않는다.

발생 가능 이상 현상 : Dirty Read, Non-Repeatable Read, Phantom Read

2. Read Committed

대부분의 RDBMS에서 사용하는 기본 격리 수준이다.
두 트랜잭션 중 한 트랜잭션이 값을 변경하고 커밋하면, 다른 한 트랜잭션이 다시 조회했을 때 값이 바뀔 수 있다.

즉, 다른 트랜잭션 내부에서, 커밋되기 전에 변경된 데이터는 읽지 않는다.

발생 가능 이상 현상 : Non-Repeatable Read, Phantom Read

3. Repeatable Read

트랜잭션 중 한 트랜잭션이 값을 변경하고 커밋하더라도, 다른 트랜잭션이 다시 조회하면 값이 바뀌지 않는다.

즉 개별 트랜잭션마다 안에서 데이터를 조회 할 때 일관된 스냅샷을 보여주도록 보장한다.

하나의 트랜잭션 안에서 데이터의 일관성이 매우 중요하거나,
여러 단계에 걸쳐 데이터를 조회하고 계산해야하는 경우에는 해당 격리 수준이 사용된다.

발생 가능 이상 현상 : Phantom Read

4. Serializable

Serializable은 가장 높은 격리 수준을 가지며,
트랜잭션이 순차적으로 실행되는 것처럼 처리되어, 다른 트랜잭션이 중간에 끼어들 수 없다.

여러 트랜잭션이 동시에 실행되더라도 결과적으로는 순차 실행된 것과 같은 결과를 보장하는 격리 수준이다.

이를 위해 DBMS는 충돌 가능성이 있는 읽기/쓰기 작업에 락을 걸거나,
충돌을 감지해 트랜잭션을 실패시킬 수 있다.


3. 격리 수준과 동시성

결국 트랜잭션의 격리 수준은 데이터 정합성동시성 사이의 균형을 조절하는 기준이다.

격리 수준이 낮을수록 여러 트랜잭션이 동시에 처리될 수 있는 범위가 넓어지기 때문에
동시성은 높아지고 성능상 이점이 생길 수 있다.

하지만 그만큼 Dirty Read, Non-Repeatable Read,
Phantom Read 같은 이상 현상이 발생할 가능성도 커진다.


반대로 격리 수준이 높을수록 트랜잭션 간 간섭은 줄어들고 데이터 정합성은 더 강하게 보장된다.
하지만 이를 위해 락을 더 오래 유지하거나, 충돌 가능성이 있는 작업을 대기시키거나,
경우에 따라 트랜잭션을 실패시켜 재시도해야 할 수 있다.
그 결과 동시성은 낮아지고 전체 처리량이 감소할 수 있다.

따라서 격리 수준은 무조건 높게 설정하는 것이 아니라,
서비스에서 요구하는 정합성 수준과 성능 요구사항을 함께 고려해 선택해야 한다.

profile
개발 및 IT기술에 대해 정리하고 기록합니다.

0개의 댓글