Foundry
데이터베이스
중급
핵심

트랜잭션 격리 수준

READ UNCOMMITTED/COMMITTED, REPEATABLE READ, SERIALIZABLE

트랜잭션 격리 수준

4가지 격리 수준

격리 수준을 올릴수록 막는 이상 현상이 늘고 동시에 처리할 수 있는 양이 줄어든다 막는 것 커밋 안 된 읽기 커밋된 읽기 반복 읽기 보장 직렬화 다 막는다 아래로 갈수록 막는 것이 늘어난다 그만큼 잠금이 길고 넓어져 동시에 처리하는 양이 줄어든다 그래서 기본값은 대개 가운데다. 필요한 곳만 올려 쓴다
격리 수준Dirty ReadNon-Repeatable ReadPhantom Read
READ UNCOMMITTEDOOO
READ COMMITTEDXOO
REPEATABLE READXXO
SERIALIZABLEXXX

이상 현상 시각화

Dirty Read (커밋 전 데이터 읽기):
T1: UPDATE bal=500 ────── ROLLBACK
T2:       READ bal=500 ← 잘못된 값!

Non-Repeatable Read:
T1: READ bal=1000 ── READ bal=500
T2:      UPDATE bal=500, COMMIT

Phantom Read (새 행 출현):
T1: SELECT cnt=5 ── SELECT cnt=6
T2:     INSERT new row, COMMIT

MySQL vs PostgreSQL 기본값

DB기본 격리 수준
MySQL (InnoDB)REPEATABLE READ
PostgreSQLREAD COMMITTED

낮춰서 얻는 것과 잃는 것

수준을 낮추면 기다림이 줄고 이상 현상이 늡니다. 어느 이상이 우리 업무에서 문제인가로 정합니다.

이상어디서 실제로 아픈가
아직 확정 안 된 값을 읽음취소될 값으로 판단한다. 대개 못 받아들인다
같은 조회가 두 값을 냄한 트랜잭션에서 합계와 목록이 안 맞는다
조건에 맞는 행이 늘어남개수 제한 검사를 통과하고도 초과한다

셋째가 가장 놓치기 쉽습니다. 예약 수를 세어 정원 미달을 확인하고 넣는 코드는 두 요청이 동시에 오면 둘 다 통과합니다. 이것은 격리 수준으로 막거나, 셀 때 잠그거나, 제약으로 막습니다.

코드로 지키는 편이 낫다

수준을 올려 막을 수도 있지만 대가가 전체에 붙습니다.

그 검사가 필요한 질의에서만 읽을 때 잠근다
가능하면 DB 제약으로 막는다. 제약은 어느 경로로 들어와도 지켜진다
그래도 실패할 수 있으니 그 오류를 다루는 코드를 둔다

둘째가 가장 튼튼합니다. 배치든 관리 화면이든 새로 만든 API 든 같은 규칙이 적용됩니다.

실무 선택 기준

  • 대부분: READ COMMITTED
  • 금융/결제: SERIALIZABLE
  • MySQL: REPEATABLE READ + MVCC
면접에서 이렇게 나옵니다

Q.트랜잭션 격리 수준 4가지를 설명해주세요

수준더티 리드반복 불가팬텀
READ UNCOMMITTED발생발생발생
READ COMMITTED막음발생발생
REPEATABLE READ막음막음발생 (InnoDB 는 대체로 막음)
SERIALIZABLE막음막음막음

위로 갈수록 안전하고 아래로 갈수록 동시성이 좋습니다. 격리를 강하게 하면 잠금 범위가 넓어져 대기가 늘어납니다.

각 이상 현상이 뜻하는 것입니다.

현상뜻
더티 리드커밋되지 않은 값을 읽는다. 롤백되면 없던 값을 본 셈
반복 불가같은 행을 두 번 읽었는데 값이 달라진다
팬텀같은 조건으로 두 번 조회했는데 행 수가 달라진다

흔한 실수: SERIALIZABLE 을 기본으로 쓰자고 답하는 것. 동시성이 크게 떨어져 처리량이 무너집니다. 대부분의 서비스는 READ COMMITTED 나 REPEATABLE READ 로 충분합니다.

Q.실무에서 주로 어떤 격리 수준을 쓰나요?

대부분 기본값을 그대로 씁니다. MySQL 은 REPEATABLE READ, PostgreSQL 과 Oracle 은 READ COMMITTED 입니다.

상황선택
일반 조회와 갱신기본값
같은 트랜잭션에서 같은 데이터를 여러 번 읽어야 한다REPEATABLE READ
정확성이 돈과 직결된다해당 구간만 명시적 잠금이나 SERIALIZABLE
통계나 리포트READ COMMITTED 로 충분. 오히려 잠금을 줄인다

중요한 것은 격리 수준을 전역으로 올리기보다 필요한 트랜잭션에만 강하게 거는 것입니다. 재고 차감이나 좌석 예약처럼 정확성이 필요한 구간에 SELECT ... FOR UPDATE 를 쓰는 방식이 흔합니다.

흔한 실수: 격리 수준만으로 모든 동시성 문제가 해결된다고 보는 것. 읽고 계산해서 쓰는 흐름은 격리 수준과 별개로 잠금이나 조건부 갱신이 필요합니다.

Q.MySQL과 PostgreSQL의 기본 격리 수준 차이는?

DB기본값이유
MySQL InnoDBREPEATABLE READ복제 방식의 역사적 이유. 문장 기반 복제에서 일관성을 지키려면 필요했다
PostgreSQLREAD COMMITTED동시성을 우선. 대부분의 애플리케이션에 충분하다

같은 이름이어도 동작이 다릅니다. InnoDB 의 REPEATABLE READ 는 갭 잠금으로 팬텀까지 대체로 막지만, 표준에서는 팬텀이 허용됩니다. PostgreSQL 의 REPEATABLE READ 는 스냅샷 격리로 구현돼 팬텀이 생기지 않습니다.

그래서 DB 를 옮길 때 격리 수준 이름만 맞추면 동작이 같을 것이라고 가정하면 안 됩니다.

흔한 실수: 표준 정의만 외워 답하는 것. 실제 제품의 구현이 표준과 다른 지점이 이 질문의 핵심입니다.

먼저 스스로 답해보고 아래 답변과 견줘보세요. 막히는 부분은 문제로 확인할 수 있어요.

읽었으면 문제로 확인해보세요

데이터베이스 문제를 풀면 틀린 문제가 자동으로 노트에 쌓입니다. 가입 없이 5문제를 먼저 풀어볼 수도 있어요.