사전 격리 수준
개념

격리 수준

gabury1

동시에 도는 트랜잭션이 서로의 중간 상태를 얼마나 보게 할지를 정한 단계입니다. 세게 잡으면 남이 하다 만 것이 안 보입니다. 대신 한꺼번에 처리되는 트랜잭션 수가 줄어듭니다. 그 사이 어디에 설지를 골라 쓰는 손잡이입니다.

상세

여럿이 같은 장부를 동시에 고칩니다. 옆 사람이 아직 연필로 적다 만 줄을 그대로 읽어도 되는지, 도장을 찍은 뒤에만 읽을지를 미리 정해 둡니다.

격리 수준은 그 약속을 데이터베이스가 대신 지키게 한 것입니다. 정해 둔 수준이 금지하는 읽기는 데이터베이스가 막습니다.

SQL(Structured Query Language, 구조화 질의 언어) 표준은 격리 수준 네 개를 정합니다. READ UNCOMMITTED · READ COMMITTED · REPEATABLE READ · SERIALIZABLE 입니다. 이 네 단계는 고전적인 직렬가능성 정의에, 일어나서는 안 되는 연산 부분수열 셋을 얹어 정의됩니다. 그 부분수열을 현상이라 부릅니다. 셋의 이름은 더티 리드 · 반복 불가능 읽기 · 팬텀입니다.

현상이 무엇인지는 표준 명세가 명시적으로 정의하지 않습니다. 다만 명세는 현상이 이상 동작으로 이어질 수 있는 연산 부분수열이라는 뜻을 내비칩니다. 그 이상 동작이란 직렬가능하지 않은 동작을 가리키는 것일 수 있습니다.

단계를 낮추는 대가로 얻는 것은 동시성입니다. 서로 다른 격리 수준으로 트랜잭션을 돌리면 응용 설계자가 정확성을 내주고 동시성과 처리량을 얻는 거래를 할 수 있습니다. 낮은 격리 수준은 트랜잭션 동시성을 높입니다. 대신 트랜잭션이 흐릿하거나 틀린 데이터베이스 상태를 보게 될 위험을 집니다.

격리 수준은 데이터베이스 전체가 아니라 트랜잭션마다 정해집니다. 그래서 가장 높은 수준에서 도는 트랜잭션 옆에서 낮은 수준의 트랜잭션이 아직 커밋되지 않은 상태에 접근하는 일이 함께 일어날 수 있습니다. 낮은 수준에서 도는 트랜잭션은 무효한 데이터를 만들어 낼 수 있습니다. 응용 설계자는 나중에 높은 격리 수준으로 도는 트랜잭션이 그 무효한 데이터에 접근해 오류를 퍼뜨리는 것을 막아야 합니다.

배경

트랜잭션을 한 번에 하나씩만 돌리면 서로 간섭할 일이 없습니다. 대신 나머지는 줄을 섭니다. 실제 데이터베이스는 여러 트랜잭션을 동시에 굴립니다. 그러면 한쪽이 아직 끝내지 않은 변경을 다른 쪽이 읽는 일이 생깁니다.

간섭을 전부 막으려면 동시성을 내줘야 합니다. 반대로 동시성을 전부 얻으려면 정확성을 내줘야 합니다. 어디까지 내줄지는 응용마다 다릅니다. 그래서 필요했던 것은 하나의 정답이 아니라 응용이 골라 쓰는 눈금이었습니다. Berenson 등의 논문은 서로 다른 격리 수준으로 트랜잭션을 돌리는 것이 응용 설계자에게 정확성과 동시성·처리량 사이의 거래를 허용한다고 적습니다.

그 눈금을 네 칸으로 끊고 이름을 붙인 것이 ANSI(American National Standards Institute, 미국 국가표준협회)와 ISO(International Organization for Standardization, 국제표준화기구)의 SQL-92 명세입니다. 가장 엄격한 칸은 직렬가능성으로 정의했습니다. 나머지 세 칸은 각각 어떤 현상을 금지하는지로 정의했습니다. 이 정의 방식 자체가 뒤에 비판을 받습니다. 같은 논문은 그 현상들과 ANSI SQL 정의가 널리 쓰이는 몇몇 격리 수준을 제대로 특징짓지 못한다는 것을 보입니다. 그 논문은 이어서 스냅샷 격리라는 다중 버전 격리 유형을 새로 정의합니다.

갈래

단계를 가르는 축은 하나입니다. 어떤 현상을 일어나지 않게 막느냐입니다. PostgreSQL 공식 문서가 정리한 현상은 넷입니다.

더티 리드        동시에 도는, 아직 커밋되지 않은 트랜잭션이 쓴 데이터를 읽는다
반복 불가능 읽기   앞서 읽은 데이터를 다시 읽었더니 그 사이 커밋된 다른 트랜잭션이 바꿔 놓았다
팬텀 읽기        검색 조건을 만족하는 행 집합을 다시 질의했더니, 최근 커밋된 다른 트랜잭션 때문에
                그 집합이 달라졌다
직렬화 이상      트랜잭션 여럿을 커밋한 결과가, 그것들을 하나씩 실행하는 어떤 순서와도 맞지 않는다

같은 문서의 표 13.1 이 단계별로 무엇이 가능한지를 적습니다.

격리 수준 더티 리드 반복 불가능 읽기 팬텀 읽기 직렬화 이상
Read uncommitted 허용되지만 PostgreSQL 에서는 아님 가능 가능 가능
Read committed 불가능 가능 가능 가능
Repeatable read 불가능 불가능 허용되지만 PostgreSQL 에서는 아님 가능
Serializable 불가능 불가능 불가능 불가능

READ UNCOMMITTED

더티 리드를 허용하는 단계입니다. Java 의 Connection.TRANSACTION_READ_UNCOMMITTED 상수 설명은 이 단계에서 더티 리드 · 반복 불가능 읽기 · 팬텀 읽기가 모두 일어날 수 있다고 적습니다. 한 트랜잭션이 바꾼 행을 그 변경이 커밋되기 전에 다른 트랜잭션이 읽을 수 있습니다. 변경이 롤백되면 읽어 간 쪽은 무효한 행을 손에 쥔 것이 됩니다.

sequenceDiagram
    participant T1 as 트랜잭션 A
    participant DB as 데이터베이스
    participant T2 as 트랜잭션 B
    T1->>DB: 행을 바꾼다
    T2->>DB: 그 행을 읽는다
    DB-->>T2: 아직 커밋 안 된 값
    T1->>DB: 롤백
    Note over T2: 손에 든 것은 무효한 행

PostgreSQL 에서는 이 단계를 요청해도 READ COMMITTED 처럼 동작합니다. 네 단계를 전부 요청할 수는 있지만 내부적으로 구현된 것은 서로 다른 세 단계뿐입니다. PostgreSQL 문서는 그것이 표준 격리 수준을 이 제품의 MVCC(Multiversion Concurrency Control, 다중 버전 동시성 제어) 구조에 대응시키는 유일하게 합리적인 방법이기 때문이라고 적습니다.

READ COMMITTED

더티 리드가 불가능한 단계입니다. 반복 불가능 읽기 · 팬텀 읽기 · 직렬화 이상은 여전히 가능합니다. PostgreSQL 의 기본 격리 수준이 이 단계입니다.

기준점은 문장입니다. SET TRANSACTION 문서는 이 단계에서 문장 하나가 자기가 시작되기 전에 커밋된 행만 볼 수 있다고 적습니다. 같은 트랜잭션 안에서도 문장이 바뀌면 기준점이 새로 잡힙니다. 그래서 같은 값을 두 번 읽는 사이에 남이 커밋한 것이 끼어들 수 있습니다.

REPEATABLE READ

더티 리드와 반복 불가능 읽기가 불가능한 단계입니다. 기준점이 문장에서 트랜잭션으로 올라갑니다. PostgreSQL 문서는 이 단계가 트랜잭션이 시작되기 전에 커밋된 데이터만 본다고 적습니다. 커밋되지 않은 데이터도, 트랜잭션이 도는 동안 동시 트랜잭션이 커밋한 변경도 보지 않습니다. SET TRANSACTION 쪽 설명은 그 기준점을 더 좁혀 적습니다. 현재 트랜잭션의 모든 문장이, 이 트랜잭션의 첫 질의나 데이터 변경 문장이 실행되기 전에 커밋된 행만 볼 수 있습니다.

표준은 이 단계에서 팬텀 읽기를 허용합니다. PostgreSQL 구현은 팬텀 읽기까지 막습니다. 표준이 특정 격리 수준에서 어떤 이상이 일어나면 안 되는지를 정할 뿐이라 더 강한 보장은 허용되기 때문입니다. PostgreSQL 문서는 이 단계가 표 13.1 의 현상 중 직렬화 이상을 뺀 나머지를 전부 막는다고 적습니다. PostgreSQL 9.1 이전에는 SERIALIZABLE 을 요청하면 여기 적힌 것과 똑같이 동작했습니다. 예전의 SERIALIZABLE 동작이 필요하면 이제 REPEATABLE READ 를 요청해야 합니다.

SERIALIZABLE

가장 엄격한 단계입니다. 커밋된 모든 트랜잭션에 대해 직렬 실행을 흉내냅니다. 동시에 실행된 것이 아니라 하나씩 차례로 실행된 것처럼 만듭니다. 표준의 정의는 직렬가능 트랜잭션 집합을 어떻게 동시에 실행하든 그것들을 어떤 순서로 하나씩 실행한 것과 같은 결과가 보장된다는 것입니다. 이 정의 때문에 앞의 현상들은 이 단계에서 하나도 일어날 수 없습니다.

Java 의 Connection.TRANSACTION_SERIALIZABLE 상수 설명은 이 단계가 REPEATABLE READ 의 금지에 더해 팬텀까지 막는다고 적습니다. 막는 상황은 이렇습니다. 한 트랜잭션이 WHERE 조건을 만족하는 행을 전부 읽습니다. 두 번째 트랜잭션이 그 조건을 만족하는 행을 하나 넣습니다. 첫 트랜잭션이 같은 조건으로 다시 읽습니다. 추가된 팬텀 행이 딸려 옵니다.

sequenceDiagram
    participant T1 as 트랜잭션 A
    participant DB as 데이터베이스
    participant T2 as 트랜잭션 B
    T1->>DB: 조건을 만족하는 행을 전부 읽는다
    T2->>DB: 그 조건을 만족하는 행을 넣는다
    T1->>DB: 같은 조건으로 다시 읽는다
    DB-->>T1: 없던 행이 하나 더 있다

값을 치릅니다. 동시에 도는 직렬가능 트랜잭션들의 읽기와 쓰기가 어떤 직렬 실행으로도 나올 수 없는 상황을 만들면, 그중 하나가 serialization_failure 오류와 함께 롤백됩니다.

예시

SET TRANSACTION 구문

SET TRANSACTION transaction_mode [, ...]
SET SESSION CHARACTERISTICS AS TRANSACTION transaction_mode [, ...]

where transaction_mode is one of:
    ISOLATION LEVEL { SERIALIZABLE | REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED }
    READ WRITE | READ ONLY
    [ NOT ] DEFERRABLE

SET TRANSACTION 은 현재 트랜잭션의 성질을 정합니다. 같은 구문에 SET SESSION CHARACTERISTICS AS TRANSACTION 형식도 나란히 적혀 있습니다. 격리 수준은 트랜잭션의 첫 질의나 데이터 변경 문장이 실행된 뒤에는 바꿀 수 없습니다. 여기서 말하는 문장은 SELECT · INSERT · DELETE · UPDATE · MERGE · FETCH · COPY 입니다. DEFERRABLE 과 SET TRANSACTION SNAPSHOT 형식은 PostgreSQL 확장입니다.

제품마다 갈리는 기본값

표준에서 기본 격리 수준은 SERIALIZABLE 입니다. PostgreSQL 에서 기본은 보통 READ COMMITTED 입니다. MySQL 의 InnoDB 는 SQL:1992 표준이 기술하는 네 격리 수준을 전부 제공합니다. 기본값은 REPEATABLE READ 입니다. 같은 SQL 문을 다른 제품으로 옮기면 격리 수준을 적지 않은 자리에서 동작이 갈립니다.

Java java.sql.Connection 의 상수

Java
Connection.TRANSACTION_NONE
Connection.TRANSACTION_READ_UNCOMMITTED
Connection.TRANSACTION_READ_COMMITTED
Connection.TRANSACTION_REPEATABLE_READ
Connection.TRANSACTION_SERIALIZABLE

TRANSACTION_NONE 은 트랜잭션이 지원되지 않는다는 표시입니다. 나머지 넷이 SQL 표준의 네 단계와 같은 이름을 씁니다. TRANSACTION_READ_COMMITTED 는 더티 리드를 막습니다. 반복 불가능 읽기와 팬텀 읽기는 일어날 수 있습니다. TRANSACTION_REPEATABLE_READ 는 더티 리드와 반복 불가능 읽기를 막습니다. 팬텀 읽기는 일어날 수 있습니다.

현재 값은 getTransactionIsolation() 으로 읽습니다. 바꾸는 것은 setTransactionIsolation(int level) 입니다. 트랜잭션이 도는 중에 이 메서드를 부르면 결과는 구현이 정합니다.

관련 항목

이것이 속하는 상위 분류

트랜잭션 · ACID(Atomicity-Consistency-Isolation-Durability, 원자성-일관성-격리성-지속성) · 격리 · 직렬가능성

이것과 맞세워지는 대립 개념

동시성 · 처리량 · 정확성

이 단계를 정의한 표준과 기구

SQL · ANSI · ISO

이것을 관리·구현하는 시스템

데이터베이스 · PostgreSQL · MySQL · InnoDB

이 단계들이 가르는 현상

더티 리드 · 반복 불가능 읽기 · 팬텀 읽기 · 직렬화 이상

격리를 실제로 구현하는 방식

MVCC · 스냅샷 격리 · 직렬가능 스냅샷 격리 · 2단계 잠금 · 낙관적 잠금 · SELECT FOR UPDATE

트랜잭션이 맞이하는 결말

커밋 · 롤백 · 데드락 · serialization_failure · 직렬화 실패 재시도

다른 이름: isolation level · transaction isolation level · 트랜잭션 격리 수준