문서의 각 단락이 어느 리비전에서 마지막으로 수정되었는지 확인할 수 있습니다. 왼쪽의 정보 칩을 통해 작성자와 수정 시점을 파악하세요.

트랜잭션 ACID 원칙 및 격리 수준 | |
이름 | ACID 원칙 |
분류 | |
핵심 구성 | 원자성(Atomicity), 일관성(Consistency), 고립성(Isolation), 지속성(Durability) |
주요 적용 대상 | 관계형 데이터베이스 관리 시스템(RDBMS), 일부 NoSQL 데이터베이스 |
관련 개념 | |
상세 설명 | |
원자성 (Atomicity) | 트랜잭션의 작업이 모두 성공하거나 모두 실패하는 'all-or-nothing' 속성. 중간 상태를 허용하지 않음. |
일관성 (Consistency) | 트랜잭션이 데이터베이스의 사전 정의된 규칙(제약 조건, 트리거 등)을 위반하지 않고 일관된 상태를 유지하도록 보장. |
고립성 (Isolation) | 동시에 실행되는 여러 트랜잭션이 서로에게 영향을 미치지 않고 독립적으로 실행되는 것처럼 보이도록 보장. 격리 수준에 따라 보장 강도가 달라짐. |
지속성 (Durability) | 커밋된 트랜잭션의 결과는 시스템 장애가 발생하더라도 영구적으로 보존됨. |
격리 수준 (Isolation Level) | ANSI/ISO SQL 표준에서 정의한 READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE의 4가지 수준. |
READ UNCOMMITTED | 가장 낮은 격리 수준. 더티 리드(Dirty Read), 반복 불가능 읽기(Non-repeatable Read), 팬텀 리드(Phantom Read) 발생 가능. |
READ COMMITTED | 대부분 DBMS의 기본 격리 수준. 더티 리드를 방지하지만, 반복 불가능 읽기와 팬텀 리드는 발생 가능. |
REPEATABLE READ | 동일 트랜잭션 내에서 동일한 SELECT 쿼리의 결과가 일관성을 유지하도록 보장. 더티 리드, 반복 불가능 읽기를 방지하지만, 팬텀 리드는 발생 가능. |
SERIALIZABLE | 가장 높은 격리 수준. 트랜잭션을 순차적으로 실행한 것과 동일한 결과를 보장. 모든 이상 현상을 방지하지만, 동시성과 성능이 가장 낮음. |
격리 수준별 현상 | 더티 리드, 반복 불가능 읽기, 팬텀 리드, 직렬화 이상(Serialization Anomaly). |
구현 메커니즘 | 락킹(Locking), MVCC(다중 버전 동시성 제어, Multi-Version Concurrency Control), 타임스탬프 순서(Timestamp Ordering) 등. |
표준 외 확장 | 일부 DBMS(예: MySQL의 InnoDB)는 REPEATABLE READ 수준에서도 갭 락(Gap Lock) 등을 통해 팬텀 리드를 방지하는 등 추가 보장을 제공. |
트레이드오프 | 격리성 강화 ↔ 동시성 및 성능 저하. 애플리케이션 요구사항에 따라 적절한 수준 선택 필요. |

트랜잭션 ACID 원칙 및 격리 수준은 데이터베이스 관리 시스템에서 데이터의 정확성과 신뢰성을 보장하기 위한 핵심 개념이다. 이 개념들은 여러 사용자가 동시에 데이터에 접근하고 변경하는 환경에서도 데이터 무결성이 훼손되지 않도록 한다.
ACID는 트랜잭션이 가져야 할 네 가지 속성인 원자성, 일관성, 격리성, 지속성의 약자이다. 이 원칙들은 트랜잭션이 모두 성공하거나 모두 실패하는 All-or-nothing 방식으로 처리되도록 하며, 데이터베이스 상태를 항상 유효하게 유지한다. 특히 격리성은 동시에 실행되는 여러 트랜잭션이 서로 간섭하지 않고 독립적으로 실행되는 것처럼 보이게 하는 속성이다.
그러나 완벽한 격리성을 유지하면 시스템의 동시성과 성능이 크게 저하될 수 있다. 따라서 실용적인 관점에서 다양한 격리 수준이 정의되었다. 각 격리 수준은 특정 종류의 동시성 문제를 허용하는 대신 성능을 높이는 트레이드오프를 제공한다.
이 문서는 ACID 원칙의 각 속성을 상세히 설명하고, 주요 격리 수준의 동작 방식, 각 수준에서 발생할 수 있는 문제점, 그리고 다양한 데이터베이스 제품별 구현 차이와 선택 가이드를 다룬다.

트랜잭션 ACID 원칙은 데이터베이스 트랜잭션이 안전하게 수행되도록 보장하기 위한 네 가지 핵심 속성을 가리킨다. 이 개념은 신뢰할 수 있는 데이터베이스 시스템의 근간을 이루며, 데이터의 정확성과 무결성을 유지하는 데 필수적이다. ACID는 각 속성의 첫 글자를 따서 만든 약어이다.
첫 번째 속성인 원자성(Atomicity)은 트랜잭션의 작업이 모두 성공하거나 모두 실패하는 'all-or-nothing' 방식을 의미한다. 트랜잭션을 구성하는 여러 연산 중 하나라도 실패하면 전체 트랜잭션은 취소되고, 데이터베이스는 트랜잭션 시작 전 상태로 되돌아간다[1]. 반대로 모든 연산이 성공적으로 완료되면 트랜잭션은 확정되어 데이터베이스에 영구적으로 반영된다[2].
두 번째 속성인 일관성(Consistency)은 트랜잭션이 성공적으로 완료되면 데이터베이스 상태가 미리 정의된 규칙과 제약 조건을 만족하도록 보장한다. 이는 트랜잭션 실행 전후에도 데이터베이스의 무결성 제약(예: 기본키, 외래키, 데이터 타입, 도메인 제약)이 위배되지 않음을 의미한다. 트랜잭션은 일관성 있는 상태에서 시작하여, 그 과정에서 일시적으로 불일치 상태가 될 수 있지만, 최종적으로는 다시 일관된 상태로 종료되어야 한다.
세 번째 속성인 격리성(Isolation)은 동시에 실행되는 여러 트랜잭션이 서로에게 영향을 미치지 않도록 분리하는 것을 목표로 한다. 각 트랜잭션은 다른 트랜잭션의 중간 상태나 미완료 작업을 인식하지 못한 채, 마치 자신만이 데이터베이스를 독점적으로 사용하는 것처럼 동작해야 한다. 완벽한 격리성은 성능 저하를 유발할 수 있어, 실제 데이터베이스 시스템은 다양한 격리 수준을 제공하여 유연성을 부여한다.
네 번째 속성인 지속성(Durability)은 한 번 커밋된 트랜잭션의 결과는 시스템 장애가 발생하더라도 영구적으로 보존됨을 보장한다. 이는 변경 사항이 비휘발성 저장 장치(예: 디스크)에 안전하게 기록되었음을 의미한다. 데이터베이스는 일반적으로 트랜잭션 로그나 체크포인트 같은 메커니즘을 통해 지속성을 구현하여, 장애 복구 후에도 커밋된 데이터를 복원할 수 있다.
원자성은 트랜잭션이 전부 실행되거나 전혀 실행되지 않아야 함을 보장하는 속성이다. 이는 트랜잭션을 구성하는 여러 작업이 하나의 원자처럼 더 이상 분할될 수 없는 단위로 취급됨을 의미한다.
트랜잭션 내의 모든 작업이 성공적으로 완료되면, 그 변경 사항은 데이터베이스에 영구적으로 반영(커밋)된다. 반면, 트랜잭션 수행 중 하나의 작업이라도 실패하면, 이미 실행된 모든 작업의 결과는 취소(롤백)되어 트랜잭션 시작 전의 상태로 되돌아간다. 이는 시스템 장애, 네트워크 문제, 제약 조건 위반, 또는 애플리케이션 로직 오류 등 어떤 이유로든 발생할 수 있다.
이 원칙의 구현은 일반적으로 트랜잭션 로그나 언두 로그를 통해 이루어진다. 시스템은 트랜잭션 시작 후 변경된 데이터의 이전 값을 별도로 저장해 두었다가, 롤백이 필요할 경우 이를 이용해 원래 상태로 복구한다. 따라서 사용자나 다른 트랜잭션은 트랜잭션이 완전히 커밋되기 전에는 중간 상태를 관찰할 수 없다.
일관성은 트랜잭션이 데이터베이스의 사전에 정의된 규칙, 즉 무결성 제약 조건을 위반하지 않고 하나의 유효한 상태에서 다른 유효한 상태로 변화시킴을 보장하는 속성이다. 이 규칙에는 기본키, 외래키, 고유 제약 조건, 체크 제약 조건 그리고 비즈니스 로직에 따른 데이터 값의 논리적 정합성 등이 포함된다. 트랜잭션의 실행 전후에 데이터베이스 상태는 항상 이러한 모든 제약 조건을 만족해야 한다.
일관성은 원자성, 격리성, 지속성과 달리 데이터베이스 시스템만으로 완전히 보장하기 어려운 측면이 있다. 시스템은 정의된 명시적 제약 조건(예: 외래키 관계)의 위반을 방지할 수 있지만, 암묵적인 비즈니스 규칙(예: '계좌 잔고는 0 이상이어야 한다')의 준수는 주로 애플리케이션 로직이 올바르게 작성되어 트랜잭션에 반영되도록 해야 한다[3]. 따라서 일관성은 데이터베이스 시스템의 제약 조건 검사 기능과 애플리케이션의 올바른 로직 설계가 협력하여 달성하는 목표이다.
트랜잭션의 실행 결과는 미리 정의된 상태로만 전이되어야 한다. 예를 들어, 은행 계좌 이체 트랜잭션에서 '총 금액의 보존'이라는 비즈니스 규칙이 있다면, 출금 계좌에서 감소된 금액과 입금 계좌에서 증가된 금액의 합은 반드시 0이 되어야 한다. 트랜잭션이 이 규칙을 위반하는 중간 상태를 생성한다면, 그 트랜잭션은 롤백되어야 하며 데이터베이스는 트랜잭션 시작 전의 일관된 상태로 되돌아간다.
격리성은 여러 트랜잭션이 동시에 실행될 때, 각 트랜잭션이 다른 트랜잭션의 중간 상태에 영향을 받지 않고 독립적으로 실행되는 것을 보장하는 특성이다. 이는 동시성 제어의 핵심 원칙으로, 시스템이 마치 각 트랜잭션이 순차적으로 실행된 것과 같은 결과를 제공하도록 한다. 데이터베이스 시스템은 락(Lock)이나 다중 버전 동시성 제어(MVCC) 같은 메커니즘을 통해 격리성을 구현한다.
완벽한 격리성은 성능 저하를 초래할 수 있으므로, 실제 데이터베이스 시스템은 다양한 격리 수준을 제공한다. 각 격리 수준은 특정 유형의 동시성 문제를 허용하는 대신 처리량을 높인다. 예를 들어, 가장 낮은 수준에서는 더티 리드가 발생할 수 있지만, 가장 높은 수준인 SERIALIZABLE은 완전한 순차 실행을 보장한다.
격리성은 ACID의 네 가지 특성 중에서 가장 유연하게 조정될 수 있는 요소이다. 애플리케이션의 요구사항에 따라 데이터 정합성과 시스템 성능 사이의 적절한 균형점을 격리 수준을 선택함으로써 찾을 수 있다.
지속성은 트랜잭션이 성공적으로 커밋되면 그 결과는 시스템 장애가 발생하더라도 영구적으로 보존되어야 함을 보장하는 속성이다. 이는 데이터베이스 관리 시스템이 제공하는 핵심적인 안정성 보장 중 하나로, 한 번 기록된 데이터는 손실되지 않는다는 신뢰의 기반이 된다. 지속성은 일반적으로 트랜잭션 로그나 WAL 같은 메커니즘을 통해 구현된다. 커밋이 완료되기 전에 모든 변경 사항은 비휘발성 저장 장치(예: 하드 디스크 드라이브 또는 SSD)에 먼저 기록된다. 이렇게 하면 시스템에 정전이나 충돌이 발생하더라도, 재시작 후 로그를 재생하여 커밋된 트랜잭션의 결과를 복구할 수 있다.
구현 방식은 데이터베이스 시스템에 따라 다르지만, 기본 원리는 커밋을 '완료'로 간주하기 전에 관련된 모든 데이터의 변경 내역을 안정적인 저장소에 확실히 기록하는 데 있다. 예를 들어, 버퍼 풀에만 존재하는 수정된 데이터 페이지를 커밋 시점에 바로 디스크에 쓰지 않고, 변경을 기술한 로그 레코드를 먼저 디스크의 로그 파일에 강제로 기록한다[4]. 이후 실제 데이터 파일에 대한 쓰기는 비동기적으로 수행될 수 있지만, 로그에 기록된 정보만으로도 트랜잭션을 재수행하거나 취소할 수 있기 때문에 지속성이 보장된다.
지속성 보장의 강도는 설정에 따라 조정될 수 있다. 일부 시스템은 성능 향상을 위해 지속성을 완화하는 옵션을 제공하기도 한다. 예를 들어, 1초마다 로그를 디스크에 동기화하거나, 배터리 백업이 있는 메모리에만 로그를 기록하는 방식 등이다. 그러나 완전한 ACID 트랜잭션을 지원하는 시스템에서는 커밋 시 로그의 디스크 동기화를 기본으로 하여 최고 수준의 지속성을 제공한다.

트랜잭션의 네 가지 핵심 속성인 ACID 중 격리성은 동시에 실행되는 여러 트랜잭션이 서로에게 영향을 미치지 않고 독립적으로 실행되는 것을 보장하는 개념이다. 그러나 이론적으로 완벽한 격리성을 구현하면 동시 처리 성능이 극도로 저하될 수 있다. 모든 트랜잭션을 순차적으로 실행하는 것과 같은 효과를 내기 때문이다. 따라서 실제 데이터베이스 시스템은 성능과 데이터 정합성 사이의 균형을 찾아 다양한 수준의 격리성을 제공한다. 이를 격리 수준이라고 부른다.
격리 수준이 필요한 근본적인 이유는 동시성 제어 문제를 효율적으로 관리하기 위해서이다. 여러 사용자가 동시에 같은 데이터에 접근하여 읽고 쓸 때, 특정 격리 수준을 설정하지 않으면 다음과 같은 문제들이 발생할 수 있다.
한 트랜잭션이 아직 커밋되지 않은 다른 트랜잭션의 중간 결과를 읽는 Dirty Read
한 트랜잭션 내에서 같은 데이터를 두 번 읽었을 때 그 값이 달라지는 Non-repeatable Read
한 트랜잭션 내에서 같은 조건으로 조회했을 때 처음에는 없던 레코드가 새로 나타나는 Phantom Read
각 격리 수준은 이러한 문제들을 어느 정도 허용하거나 방지함으로써 잠금의 범위와 지속 시간을 조절하고, 결과적으로 시스템의 처리량과 응답 시간에 직접적인 영향을 미친다. 개발자는 애플리케이션의 비즈니스 요구사항(예: 정확성이 최우선인 금융 시스템 vs 읽기 일관성이 다소 낮아도 되는 캐시 시스템)에 따라 적절한 격리 수준을 선택하여 성능과 정확성의 최적점을 찾아야 한다.

트랜잭션의 격리성을 보장하는 정도를 정의한 표준적인 수준은 네 가지이다. 이 수준들은 낮은 격리성과 높은 성능에서 높은 격리성과 낮은 성능 사이의 스펙트럼을 형성한다. 각 수준은 특정한 동시성 제어 문제를 방지하도록 설계되었다.
가장 낮은 수준인 READ UNCOMMITTED는 트랜잭션이 다른 트랜잭션이 아직 커밋하지 않은, 즉 언두 로그에 의존하지 않은 데이터를 읽을 수 있게 허용한다. 이는 가장 빠른 성능을 제공하지만 데이터의 정확성을 심각하게 훼손할 위험이 있다. 그 다음 수준인 READ COMMITTED는 오직 커밋된 데이터만 읽을 수 있도록 보장한다. 이는 Dirty Read 문제를 방지하며, 많은 데이터베이스 시스템의 기본 격리 수준으로 채택된다.
더 높은 수준인 REPEATABLE READ는 하나의 트랜잭션 내에서 동일한 쿼리를 반복 실행할 때 일관된 결과를 보장한다. 이 수준은 Non-repeatable Read 현상을 방지하기 위해, 트랜잭션이 읽은 데이터에 대한 읽기 잠금을 유지하거나 다중 버전 동시성 제어를 통해 스냅샷을 고정하는 방식으로 구현된다. 가장 높은 수준인 SERIALIZABLE은 트랜잭션들을 순차적으로 실행한 것과 동일한 결과를 보장한다. 이는 완전한 격리를 제공하여 Phantom Read를 포함한 모든 이상 현상을 방지하지만, 동시성과 성능 저하가 가장 크다.
각 격리 수준이 방지하는 문제를 요약하면 다음과 같다.
격리 수준 | Dirty Read | Non-repeatable Read | Phantom Read |
|---|---|---|---|
READ UNCOMMITTED | 가능 | 가능 | 가능 |
READ COMMITTED | 방지 | 가능 | 가능 |
REPEATABLE READ | 방지 | 방지 | 가능 |
SERIALIZABLE | 방지 | 방지 | 방지 |
READ UNCOMMITTED는 트랜잭션 격리 수준 중 가장 낮은 수준이다. 이 수준에서는 한 트랜잭션이 아직 커밋되지 않은, 즉 다른 트랜잭션에 의해 변경 중인 데이터를 읽을 수 있다.
이 격리 수준은 일반적으로 락이나 잠금을 사용하지 않거나 최소한의 잠금만을 사용하여 구현된다. 읽기 작업은 다른 트랜잭션이 데이터에 배타적 잠금을 걸어 변경을 방지하고 있지 않는 한, 대기하지 않고 즉시 수행된다. 이로 인해 동시성은 매우 높아지지만, 데이터의 정합성에 심각한 문제가 발생할 수 있다.
특징 | 설명 |
|---|---|
동시성 | 매우 높음 |
데이터 정합성 | 매우 낮음 |
주요 발생 문제 | |
잠금 사용 | 최소화 또는 없음 |
이 수준에서 가장 두드러지게 발생하는 문제는 Dirty Read이다. 트랜잭션 A가 어떤 데이터를 변경한 후 아직 커밋하지 않은 상태에서, 트랜잭션 B가 그 변경된 데이터를 읽을 수 있다. 만약 이후 트랜잭션 A가 롤백된다면, 트랜잭션 B는 실제 데이터베이스에 존재하지 않았던(혹은 일시적으로 존재했던) 잘못된 데이터를 읽고 이를 기반으로 작업을 수행한 상태가 된다. 이는 데이터의 논리적 일관성을 심각하게 훼손한다.
따라서 READ UNCOMMITTED는 데이터 정확성이 극히 중요하지 않은 통계 정보 수집이나, 변경 중인 데이터를 실시간으로 모니터링하는 특수한 경우를 제외하고는 실무에서 거의 사용되지 않는다. 대부분의 현대 관계형 데이터베이스 관리 시스템에서는 더 높은 격리 수준을 기본값으로 설정한다.
READ COMMITTED는 대부분의 데이터베이스 관리 시스템에서 기본적으로 채택하는 격리 수준이다. 이 수준은 트랜잭션이 다른 트랜잭션에 의해 커밋된 데이터만 읽을 수 있도록 보장한다. 즉, 아직 커밋되지 않은, 더티 리드가 발생할 수 있는 중간 상태의 데이터를 읽지 않는다.
구현 방식은 데이터베이스마다 다르지만, 일반적으로 락이나 MVCC를 사용한다. 락 기반 구현에서는 트랜잭션이 데이터를 읽을 때 공유 락을 걸고, 해당 데이터에 대한 배타적 락이 걸려 있는 경우 커밋될 때까지 대기한다. MVCC를 사용하는 시스템에서는 트랜잭션 시작 시점이나 쿼리 실행 시점의 스냅샷을 기준으로 이미 커밋된 데이터 버전만을 조회한다.
이 격리 수준에서는 더티 리드는 방지되지만, 논리적 일관성에 대한 다른 문제들이 발생할 수 있다. 한 트랜잭션 내에서 동일한 쿼리를 두 번 실행했을 때, 그 사이에 다른 트랜잭션이 해당 데이터를 수정하고 커밋하면 서로 다른 결과를 읽게 되는 논리적 일관성이 발생한다. 또한 새로운 행이 추가되는 팬텀 리드 현상도 가능하다.
주요 데이터베이스의 기본 설정은 다음과 같다.
데이터베이스 시스템 | 기본 격리 수준 |
|---|---|
READ COMMITTED | |
READ COMMITTED | |
READ COMMITTED | |
REPEATABLE READ |
REPEATABLE READ는 트랜잭션 격리 수준 중 하나로, 하나의 트랜잭션 내에서 동일한 SELECT 쿼리를 반복 수행할 때 항상 동일한 결과를 보장하는 수준이다. 이는 READ COMMITTED 수준보다 강력한 격리를 제공하며, 주로 Non-repeatable Read 현상을 방지하기 위해 설계되었다. 많은 데이터베이스 시스템에서 기본 격리 수준으로 채택하고 있다[5].
이 수준에서는 트랜잭션이 시작된 후 다른 트랜잭션이 커밋한 갱신 내용이 읽기 작업에 영향을 미치지 않도록 한다. 이를 구현하는 일반적인 메커니즘은 다중 버전 동시성 제어나 읽기 잠금을 사용하는 것이다. 예를 들어, 트랜잭션 A가 특정 조건으로 데이터를 읽은 후, 트랜잭션 B가 해당 데이터를 수정하고 커밋하더라도, 트랜잭션 A가 다시 같은 조건으로 읽을 때는 처음 읽은 데이터와 동일한 값을 유지한다.
격리 수준 | Dirty Read 방지 | Non-repeatable Read 방지 | Phantom Read 방지 |
|---|---|---|---|
READ UNCOMMITTED | X | X | X |
READ COMMITTED | O | X | X |
REPEATABLE READ | O | O | X |
SERIALIZABLE | O | O | O |
그러나 REPEATABLE READ 수준에서도 완벽한 격리는 아니다. 이 수준은 주로 이미 읽은 행의 값이 변경되는 것을 방지하지만, 트랜잭션 도중 새로운 행이 추가되는 Phantom Read 현상은 발생할 수 있다. 일부 데이터베이스는 이 문제를 완화하기 위해 추가적인 메커니즘을 도입하기도 한다[6].
SERIALIZABLE은 트랜잭션 격리 수준 중 가장 높은 수준의 격리성을 보장하는 수준이다. 이 수준에서는 여러 트랜잭션이 동시에 실행되더라도, 그 결과가 순차적으로(Serial) 하나씩 실행된 것과 동일하게 보장된다. 이는 완벽한 격리를 의미하며, 동시성 제어의 이상 현상인 Dirty Read, Non-repeatable Read, Phantom Read가 모두 발생하지 않는다.
이를 구현하기 위해 데이터베이스 시스템은 일반적으로 잠금(Locking) 기법을 적극적으로 사용한다. 대표적인 방법은 범위 잠금(Range Lock) 또는 갭 잠금(Gap Lock)을 적용하여 트랜잭션이 읽은 데이터 집합에 대해 다른 트랜잭션이 쓰기 작업(INSERT, UPDATE, DELETE)을 수행하지 못하도록 차단하는 것이다. 예를 들어, WHERE 조건에 맞는 모든 행을 읽는 쿼리는 해당 조건을 만족하는 현재 존재하는 행뿐만 아니라, 앞으로 그 조건에 맞게 삽입될 수 있는 모든 행의 범위까지 잠근다. 이로 인해 다른 트랜잭션이 그 범위에 새로운 행을 삽입하는 Phantom Read 현상을 근본적으로 방지한다.
SERIALIZABLE 수준은 데이터 정합성을 최우선으로 하는 환경에서 필수적이다. 금융 시스템의 계좌 이체, 항공 예약 시스템의 좌석 배정, 재고 관리 시스템의 정확한 수량 관리 등과 같이 데이터의 완전한 일관성이 요구되는 비즈니스 로직에 적합하다. 그러나 가장 높은 수준의 안전성을 제공하는 대신, 동시에 실행될 수 있는 트랜잭션의 수가 크게 제한되고, 교착상태(Deadlock)가 발생할 확률이 높아지며, 전체 시스템의 처리 성능(Throughput)이 다른 격리 수준에 비해 현저히 떨어진다.
격리 수준 | Dirty Read | Non-repeatable Read | Phantom Read | 동시성 |
|---|---|---|---|---|
READ UNCOMMITTED | 가능 | 가능 | 가능 | 높음 |
READ COMMITTED | 방지 | 가능 | 가능 | 보통 |
REPEATABLE READ | 방지 | 방지 | 가능 | 보통 |
SERIALIZABLE | 방지 | 방지 | 방지 | 낮음 |
따라서 SERIALIZABLE 수준의 사용은 신중하게 결정해야 한다. 애플리케이션의 요구사항을 정확히 분석하여, 반드시 완전한 직렬화가 필요한 경우에만 선택하는 것이 바람직하다. 많은 경우, 낙관적 잠금(Optimistic Locking)이나 애플리케이션 수준의 동시성 제어 등 다른 방법으로 일관성 요구사항을 충족시킬 수 있다.

각 격리 수준은 특정한 부작용을 방지하도록 설계되었지만, 낮은 수준에서는 여러 가지 문제가 발생할 수 있다. 이러한 문제는 데이터의 정확성과 일관성에 영향을 미치는 현상으로, 주로 Dirty Read, Non-repeatable Read, Phantom Read 세 가지로 구분된다.
문제 현상 | 설명 | 발생 가능한 격리 수준 |
|---|---|---|
Dirty Read | 한 트랜잭션이 아직 커밋되지 않은 다른 트랜잭션의 수정 데이터를 읽는 현상이다. 롤백될 수 있는 미확정 데이터를 읽게 되어 논리적 오류를 초래할 수 있다. | |
Non-repeatable Read | 한 트랜잭션 내에서 같은 데이터를 두 번 읽었을 때, 그 사이에 다른 트랜잭션이 해당 데이터를 수정 및 커밋함으로써 두 조회 결과가 달라지는 현상이다. | |
Phantom Read | 한 트랜잭션 내에서 같은 조건으로 데이터를 두 번 조회했을 때, 첫 번째 조회 시에는 없던 레코드가 다른 트랜잭션의 삽입 작업으로 인해 두 번째 조회 시에 나타나는 현상이다. 집계 결과나 목록이 일관되지 않게 된다. |
이러한 문제들은 격리 수준을 높여 해결할 수 있다. READ COMMITTED 수준은 Dirty Read를 방지하며, REPEATABLE READ 수준은 Dirty Read와 Non-repeatable Read를 추가로 방지한다. 가장 엄격한 SERIALIZABLE 수준은 위의 세 가지 문제를 모두 방지하지만, 그만큼 동시성 처리 성능이 저하되는 트레이드오프가 존재한다.
Dirty Read는 하나의 트랜잭션이 아직 커밋되지 않은, 다른 트랜잭션의 중간 변경 데이터를 읽는 현상을 말한다. 이는 낮은 격리 수준에서 발생하는 대표적인 문제 중 하나이다. 예를 들어, 트랜잭션 A가 어떤 데이터를 수정하는 동안, 트랜잭션 B가 트랜잭션 A의 커밋 여부와 관계없이 그 수정 중인 데이터를 읽을 수 있다. 만약 트랜잭션 A가 이후 롤백된다면, 트랜잭션 B는 실제 데이터베이스에 존재하지 않거나 일시적이었던 잘못된 데이터를 기반으로 동작하게 된다.
이 문제는 데이터의 정확성과 신뢰성을 심각하게 훼손한다. 트랜잭션 B가 읽은 더티 데이터를 바탕으로 추가적인 계산이나 결정을 내리거나, 다른 데이터를 변경할 경우 그 오류는 전파된다. 금융 시스템에서 잔액을 잘못 읽거나, 재고 관리 시스템에서 존재하지 않는 재고를 확인하는 등의 오류로 이어질 수 있다.
대부분의 현대 데이터베이스 관리 시스템은 기본 격리 수준으로 READ COMMITTED 이상을 사용하여 Dirty Read를 방지한다. READ COMMITTED 수준에서는 트랜잭션이 다른 트랜잭션에 의해 커밋된 데이터만 읽을 수 있도록 보장한다. 따라서 Dirty Read 문제는 주로 의도적으로 격리 수준을 READ UNCOMMITTED로 낮춘 환경이나, 해당 수준을 기본값으로 사용하는 특정 시스템에서 주로 발생한다.
격리 수준 | Dirty Read 발생 가능 여부 |
|---|---|
READ UNCOMMITTED | 가능 |
READ COMMITTED | 불가능 |
REPEATABLE READ | 불가능 |
SERIALIZABLE | 불가능 |
Non-repeatable Read는 한 트랜잭션 내에서 동일한 데이터를 두 번 이상 읽을 때, 그 사이에 다른 트랜잭션이 해당 데이터를 수정하고 커밋함으로써 읽은 값이 달라지는 현상이다.
이 문제는 주로 READ COMMITTED 격리 수준에서 발생한다. 예를 들어, 트랜잭션 A가 특정 사용자의 잔액을 읽어 100원이라는 값을 얻었다고 가정한다. 그 후 트랜잭션 B가 같은 사용자의 잔액을 150원으로 업데이트하고 커밋한다. 이후 트랜잭션 A가 다시 같은 사용자의 잔액을 읽으면, 이번에는 150원이라는 다른 값을 얻게 된다. 트랜잭션 A 내부에서 동일한 쿼리의 결과가 일관되지 않게 되어 데이터의 신뢰성에 문제를 일으킨다.
격리 수준 | Non-repeatable Read 발생 여부 |
|---|---|
READ UNCOMMITTED | 발생 가능 |
READ COMMITTED | 발생 가능 |
REPEATABLE READ | 방지됨 |
SERIALIZABLE | 방지됨 |
이 현상을 방지하기 위해서는 더 높은 격리 수준인 REPEATABLE READ 이상을 사용해야 한다. REPEATABLE READ 수준에서는 트랜잭션이 처음 읽은 데이터에 대해 읽기 잠금을 걸거나 다중 버전 동시성 제어를 통해 스냅샷을 유지함으로써, 트랜잭션 내에서 동일한 데이터를 항상 같은 값으로 읽을 수 있도록 보장한다.
팬텀 리드는 트랜잭션이 동일한 쿼리를 두 번 수행했을 때, 첫 번째 실행에서는 없었던 새로운 행(레코드)이 두 번째 실행에서 나타나는 현상이다. 이는 다른 트랜잭션이 첫 번째 트랜잭션의 두 번의 읽기 사이에 새로운 데이터를 삽입(INSERT)했기 때문에 발생한다. 결과 집합이 '유령(Phantom)'처럼 나타나거나 사라지는 것처럼 보인다.
팬텀 리드는 주로 범위 쿼리나 집계 쿼리에서 발생한다. 예를 들어, 나이가 20세 이상인 사용자 수를 세는 쿼리를 실행한 후, 다른 트랜잭션이 나이가 20세인 새로운 사용자를 추가하면, 동일한 쿼리를 다시 실행했을 때 이전과 다른 결과를 얻게 된다. Non-repeatable Read가 기존 행의 값이 변경되는 것에 초점을 맞춘다면, 팬텀 리드는 결과 집합에 속하는 행 자체의 개수가 변하는 문제에 초점을 맞춘다.
격리 수준 | 팬텀 리드 발생 가능성 |
|---|---|
발생 가능 | |
발생 가능 | |
대부분의 DBMS에서 방지* | |
방지 |
일부 데이터베이스 시스템(예: MySQL의 InnoDB 스토리지 엔진)은 REPEATABLE READ 격리 수준에서도 넥스트 키 락(Next-Key Locking)이라는 방식을 사용하여 팬텀 리드를 방지한다[7]. 그러나 오라클이나 PostgreSQL과 같은 시스템의 기본 격리 수준인 READ COMMITTED에서는 팬텀 리드가 발생할 수 있다.

트랜잭션 격리 수준은 SQL 표준에 의해 정의되지만, 실제 데이터베이스 관리 시스템마다 구현 방식과 세부 동작에 차이가 존재한다. 이는 각 DBMS가 성능, 데이터 무결성, 동시성 처리 등 설계 목표에 따라 다른 접근 방식을 채택하기 때문이다.
주요 관계형 데이터베이스 관리 시스템의 구현 특징은 다음과 같다.
DBMS | 기본 격리 수준 | 주요 구현 특징 |
|---|---|---|
MySQL (InnoDB) | REPEATABLE READ | 기본적으로 스냅샷 격리를 사용하여 팬텀 리드를 방지한다. 넥스트 키 락을 통해 갭을 잠가 팬텀 리드를 차단한다[8]. |
READ COMMITTED | MVCC를 기반으로 하며, 기본 수준에서도 스냅샷을 사용한다. REPEATABLE READ 수준에서는 트랜잭션 시작 시점의 스냅샷을 고정 사용하며, SERIALIZABLE 수준에서는 직렬화 실패 시 애플리케이션에 재시도 로직이 필요하다. | |
READ COMMITTED | MVCC를 사용하며, 기본적으로 팬텀 리드를 허용하지 않는 READ COMMITTED를 제공한다. 공식적인 REPEATABLE READ 수준은 없으며, SERIALIZABLE 수준이나 명시적 잠금을 통해 유사한 격리를 달성한다. | |
READ COMMITTED | 기본적으로 잠금 기반 동시성 제어를 사용한다. READ COMMITTED 수준에서도 잠금을 통해 더티 리드를 방지한다. 스냅샷 격리 수준을 별도로 활성화하여 MVCC 방식의 읽기도 지원한다. |
이러한 차이로 인해 동일한 SQL 문장과 격리 수준 설정이라도 다른 DBMS에서는 서로 다른 동시성 문제가 발생하거나 성능에 미치는 영향이 달라질 수 있다. 예를 들어, MySQL의 REPEATABLE READ는 표준보다 강한 격리를 제공하는 경우가 많고, Oracle은 표준 격리 수준과 완전히 일치하지 않는 모델을 가진다. 따라서 애플리케이션 개발 시 목표 DBMS의 구체적인 동작 방식을 이해하고, 필요에 따라 낙관적 잠금이나 비관적 잠금 등의 패턴을 추가로 적용해야 한다.

적절한 격리 수준 선택은 애플리케이션의 정확성, 성능, 동시성을 균형 있게 맞추는 핵심 과제이다. 선택은 주로 데이터 일관성 요구사항과 동시 처리 성능 사이의 트레이드오프를 고려하여 이루어진다. 높은 격리 수준은 데이터 정합성을 강력히 보장하지만 동시성과 처리량을 저하시키는 반면, 낮은 격리 수준은 성능은 우수하지만 다양한 동시성 제어 문제가 발생할 위험이 있다.
일반적인 선택 기준은 다음과 같다. 읽기 작업이 매우 많고, 최신 커밋된 데이터만 필요하며, 약간의 Non-repeatable Read가 허용되는 경우 READ COMMITTED 수준이 널리 사용된다. 이는 대부분의 RDBMS의 기본 설정이다. 금융 거래나 정산과 같이 한 트랜잭션 내에서 동일한 데이터를 반복 읽을 때 값이 변하지 않아야 하는 경우 REPEATABLE READ 수준을 고려한다. SERIALIZABLE 수준은 가장 엄격하여 순차 실행과 동등한 격리를 제공하지만, 성능 저하가 크므로 경매 시스템의 남은 수량 관리나 잔고 이체와 같이 최고 수준의 정확성이 요구되는 매우 제한적인 시나리오에서만 사용한다. READ UNCOMMITTED 수준은 거의 사용되지 않으며, 정확성이 중요하지 않은 대략적인 통계 집계나 실시간성이 매우 중요한 모니터링 등 특수한 경우에 한해 고려할 수 있다.
애플리케이션의 특정 요구사항에 따라 다음과 같은 실용적인 선택 가이드를 적용할 수 있다.
주요 고려 사항 | 권장 격리 수준 | 이유 |
|---|---|---|
높은 동시성과 성능이 최우선, 약간의 비일관성 허용 | 가장 일반적인 균형점을 제공하며, Dirty Read를 방지함 | |
보고서 생성 또는 분석 쿼리에서 동일한 스냅샷 필요 | 트랜잭션 내 조회 결과의 일관성을 보장함 | |
엄격한 정확성 요구 (예: 잔고 이체, 재고 관리) | 모든 동시성 문제를 방지하여 정확성을 최대화함 | |
읽기 전용 작업, 데이터 정확성보다 속도가 중요 | READ UNCOMMITTED (매우 제한적 사용) | 락 오버헤드가 거의 없어 가장 빠른 읽기 성능 제공 |
최종 결정은 특정 데이터베이스 관리 시스템의 구현 방식, 하드웨어 성능, 애플리케이션의 트랜잭션 패턴을 종합적으로 테스트한 후 내리는 것이 바람직하다. 많은 시스템에서는 기본 격리 수준을 유지하면서, 특정 쿼리에만 필요에 따라 더 높거나 낮은 수준의 힌트를 주는 방식으로 세밀하게 제어한다.
