트랜잭션 격리 수준(1) : SQL-92

mseo39·2026년 1월 26일

TIL

목록 보기
19/19
post-thumbnail

트랜잭션 격리 수준에 대해서는 알고있지만 정작 어떤 기준으로 선택되는지에 대한 이해가 부족해 공부한 글입니다

트랜잭션 동시 실행 시 발생하는 이상현상

SQL-92 표준에서는 격리 수준을 나누는 기준으로 다음 3가지 현상을 정의합니다

1. Dirty Read : Commit 되지 않은 데이터를 읽음

초기 상태: x=10. y=20

순서Transaction 1Transaction 2설명
1Read(x) → 10 읽음T1이 현재 x 값을 확인
2Write(y = 70)T2가 y를 70으로 바꿨지만 아직 커밋 안 함
3Read(y) → 70 읽음[문제 발생] T1이 T2의 임시 값(70)을 읽음
4Write(Sum = 80)T1은 10 + 70 = 80이라고 계산하고 저장
5RollbackT2에 문제가 생겨 y20으로 롤백

확정되지 않은 데이터는 읽으면 안된다

2. Non-Repeatable Read : 한 트랜잭션에서 같은 데이터 조회 시 값이 달라짐

초기 상태: x=10

순서Transaction 1Transaction 2설명
1Read(x) → 10 읽음T1이 현재 x 값을 확인
2Read(x) → 10 읽음T2도 x 값을 확인
3Write(x = 50)T2가 x에 40을 더해서 50으로 바꿈
4Commit
5Read(x) → 50 읽음T1이 다시 x를 읽었는데 아까와 다른 50이 나옴

하나의 트랜잭션 내에서는 조회를 반복해도 값이 항상 같아야 한다

💡왜 문제가 될까..? : 쇼핑몰 결제 가격 변동

순서Transaction 1 (구매자)Transaction 2 (관리자)설명
1Read(Price) → 100 읽음사용자가 화면에서 100만 원을 확인하고 '결제하기' 버튼 클릭
2Write(Price = 150)그 찰나에 관리자가 가격을 150만 원으로 변경
3Commit
4Read(Price) → 150 읽음시스템이 PG사 결제 연동을 위해 가격을 다시 조회했는데 150만 원이 됨 (Non-Repeatable Read 발생)
5Pay(150)사용자는 100만 원을 보고 눌렀는데 실제로는 150만 원이 결제됨

👉 트랜잭션이 시작될 때의 그 조건(100만 원)으로 끝까지 처리가 되어야 "사용자가 동의한 내용"과 "실제 처리 결과"가 일치됨

3. Phantom read : 한 트랜잭션에서 동일한 조건으로 조회 시 없던 데이터가 생김

초기 상태: v=10인 데이터는 t1뿐

순서Transaction 1Transaction 2설명
1Read(v = 10) → t1T1이 현재 v=10 조건 확인
2Write(t2, v = 10)T2가 v=10인 데이터 삽입
3Commit
4Read(v = 10) → t1, t2아까는 없었던 t2가 추가됨

하나의 트랜잭션 내에서는 동일한 조건으로 조회를 반복해도 결과 집합이 항상 같아야 함

💡왜 문제가 될까..? : 수강신청 정원 초과

순서Transaction 1 (나)Transaction 2 (경쟁자)설명
1Read(count) → 9명 읽음현재 9명이므로 "신청 가능(남은 자리 1)" 하다고 판단함
2Insert(학생 B)그 찰나의 순간에 경쟁자가 수강 신청(INSERT)을 함
3Commit경쟁자의 신청이 확정됨 (이제 실제 DB는 10명/만석)
4Insert(나)나는 아까 확인한 "9명" 정보를 믿고 그대로 신청(INSERT)
5Commit시스템이 막지 않고 저장해버림.
결국 수강생은 11명이 됨

👉 막아야 하는 이유: 남는 자리가 있다는 판단이 남이 데이터를 끼워 넣음으로써 거짓말이 되어버림


Isolation Level

위에서 언급한 이상 현상을 모두 막으면 동시 처리 가능한 트랜잭션 수가 줄어들어 DB의 전체 처리량이 낮아집니다 그래서 일부 이상 현상은 허용하는 몇가지 레벨을 만들어서 사용자가 필요에 따라 적절하게 선택할 수 있도록 4가지 격리 수준을 제공합니다

레벨특징핵심예시
READ UNCOMMITTED커밋도 안 된 데이터를 읽어옴데이터의 정확성보다는 동시성과 속도가 최우선일 때 사용실시간 로그, '좋아요' 수 집계, 도로 교통정보
READ COMMITTED커밋이 완료된 데이터만 읽음. 하지만 다른 트랜잭션이 커밋하면 값이 즉시 바뀜일반적인 웹 서비스의 표준으로 최신 값을 반영할 때 사용주문 결제 대기(Polling), 실시간 재고 조회, 일반 게시판
REPEATABLE READ트랜잭션이 시작된 시점의 데이터를 끝까지 유지함. 다른 곳에서 변경해도 내 조회 결과는 변하지 않음트랜잭션 내에서 데이터 정합성이 중요할 때 사용일일 매출 정산, 통계 리포트, 데이터 백업
SERIALIZABLE한 번에 하나만 독립적으로 수행 (병렬 처리 X, 직렬 처리 O). 가장 엄격한 격리 수준데이터의 유무나 수량을 정확하게 판단하고 진입해야 할 때 사용선착순 이벤트, 티켓팅, 재고 관리

쉽게 이야기 하자면 누군가 데이터를 수정하고 있을 때 데이터를 읽는 사람에게 어느 시점의 데이터를 보여줄 것인가를 결정하는 규칙

  • READ UNCOMMITTED: 고치고 있는 중인 거 그냥 보여줘
  • READ COMMITTED: 다 고치고 도장 찍으면 그때 보여주고 그 전까진 옛날 거 보여줘
  • REPEATABLE READ: 내가 처음에 읽기 시작했을 때랑 똑같은 것만 계속 보여줘
  • SERIALIZABLE: 내가 읽는 동안에 다른 사람의 쓰기를 아예 막아버립다

내 프로젝트에는..?

공부한 내용을 바탕으로 이전에 만들었던 지점 도어락 패스워드 중앙 관리 시스템 구축, 블랙리스트 기능에는 어떤 격리 수준이 적합할지 고민해보니 READ COMMITTED가 가장 적합하다고 판단했습니다

  • 데이터의 유무나 수량을 정확하게 판단하고 진입해야 할 로직이 없고
  • 트랜잭션 수행 중이라도 커밋된 최근 값을 조회해야하기 때문입니다

앞으로 공부할 내용

이번에는 격리 수준의 선택 기준에 대해 정리했습니다. 공부하다 보니 더 깊이 파봐야 할 주제들이 생겨, 다음 포스팅에서는 아래 내용들을 다뤄보려 합니다

  1. 다른 이상 현상들: 오늘 다룬 3가지 외에 Dirty Write, Lost Update 등 다른 현상들은 무엇인지?
  2. DBMS별 차이: DB 엔진마다 격리 수준 구현 방식이 어떻게 다른지?
  3. 격리 수준의 대안: 실무에서 격리 수준 변경 대신 많이 쓴다는 낙관적 락 / 비관적 락

참고자료

profile
하루하루 성실하게

0개의 댓글