CodeGym /행동 /SQL SELF /트랜잭션 격리 수준 소개

트랜잭션 격리 수준 소개

SQL SELF
레벨 39 , 레슨 4
사용 가능

카페에서 일한다고 상상해봐. 한 명의 서버가 주방에 케이크 재고를 확인하고, 다른 서버는 새 손님 주문을 받고 있어. 이상적인 세상에선 둘 다 같은 케이크 개수 데이터를 보고 실수(예: "이중 예약")를 피해야 해. 근데 현실에선 동시 작업 때문에 문제가 생길 수 있지.

여기 대표적인 세 가지 골치 아픈 상황이 있어:

  1. Dirty Read (더러운 읽기): 한 쿼리가 다른 쿼리에서 아직 커밋되지 않은 변경사항을 볼 수 있어. 만약 그 변경이 롤백되면, 첫 번째 쿼리는 마치 첫 면접 보는 학생처럼 순진하게 당하는 거지.

  2. Non-Repeatable Read (반복 불가 읽기): 한 쿼리가 같은 데이터를 두 번 읽는데, 그 사이에 누군가 데이터를 바꿔버려. 이건 마치 기차역에 가서 시간표를 보고, 1분 뒤에 다시 갔더니 기차가 취소된 걸 발견하는 거랑 비슷해. 아니면 네 표가 네가 돈 찾는 사이에 팔려버린 거지 :)

  3. Phantom Read (팬텀 읽기): 한 쿼리가 일부 행만 보는데, 두 번 실행하는 사이에 누군가 새로운 행을 추가해서 결과가 달라져. 이건 마치 네 회사가 입찰에서 떨어졌는데, 나중에 보니 네 거랑 시장 부인 것만 남고 나머지 신청이 다 취소된 상황이랑 비슷해.

트랜잭션 격리 수준

이제 문제를 알았으니, PostgreSQL이 이걸 해결하려고 제공하는 트랜잭션 격리 수준이라는 도구를 보자. 이건 동시 트랜잭션끼리 어떻게 상호작용할지 규칙을 정하는 거야. 격리 수준이 높을수록 트랜잭션끼리 방해받을 확률이 줄어. 근데 그만큼 "서비스 속도", 즉 성능이 떨어질 수 있어.

PostgreSQL의 격리 수준

  1. Read Uncommitted (커밋 안 된 데이터 읽기):

    • 아직 커밋되지 않은 변경사항도 읽을 수 있어(맞아, 이게 바로 더러운 읽기야).
    • PostgreSQL에선 사실 이 수준이 Read Committed로 구현돼서, 순수하게 지원하진 않아. PostgreSQL은 이 방식이 너무 위험하다고 생각해서 안 쓰는 거지.
  2. Read Committed (커밋된 변경사항 읽기):

    • 더러운 읽기를 막아줘.
    • 트랜잭션은 명령 실행 시점에 커밋된 데이터만 볼 수 있어.
    • 그래도 Non-Repeatable ReadPhantom Read는 발생할 수 있어.
  3. Repeatable Read (반복 가능한 읽기):

    • 네가 읽은 데이터는 트랜잭션 동안 변하지 않는 걸 보장해.
    • 더러운 읽기랑 반복 불가 읽기를 막아줘.
    • 그래도 팬텀 행은 여전히 생길 수 있어.
  4. Serializable (직렬화):

    • 트랜잭션들이 마치 하나씩 순서대로 실행된 것처럼 보장해줘.
    • 세 가지 문제(더러운 읽기, 반복 불가 읽기, 팬텀 행)를 모두 막아줘.
    • 가장 엄격하고, 가장 느린 격리 수준이야.

트랜잭션 격리가 왜 중요할까?

이제 인터넷 쇼핑몰 데이터베이스를 상상해봐. 천 명의 사용자가 동시에 주문을 넣고 있어. 격리 수준을 제대로 안 맞추면 "사라진" 상품이나 중복 주문 같은 엄청난 충돌이 생길 수 있어.

적절한 격리 수준을 고르면 동시 트랜잭션을 잘 다루면서 성능이랑 데이터 무결성 사이에서 균형을 잡을 수 있어. 예를 들어:

  • 분석 시스템에선 보통 최소 격리 수준(Read Committed 등)을 써. 데이터 정확성이 항상 중요하진 않으니까.
  • 금융 시스템에선 Serializable 수준이 더 좋아. 계산 오류나 중복 작업을 피하려면 말이지.

격리 수준 적용 예시

  1. Read Committed

이 수준에선 롤백될 수도 있는 데이터를 읽는 일은 없어.

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;

-- 계좌 데이터 읽기.
SELECT balance FROM accounts WHERE account_id = 1;

-- 다른 트랜잭션이 balance를 업데이트하면, 이 데이터도 바로 바뀜.
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;

COMMIT;
  1. Repeatable Read

이 수준은 네가 읽은 데이터가 트랜잭션 내내 변하지 않게 해줘.

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;

-- 계좌 데이터 읽기.
SELECT balance FROM accounts WHERE account_id = 1;

-- 다른 트랜잭션이 balance를 바꿔도, 넌 그 변화를 못 봐.
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;

COMMIT;
  1. Serializable

이 수준에선 트랜잭션이 시스템에서 유일하게 동작하는 것처럼 처리돼.

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN;

-- 계좌 데이터 읽기.
SELECT balance FROM accounts WHERE account_id = 1;

-- 네 트랜잭션이 끝날 때까지, 이 데이터를 바꾸려는 다른 트랜잭션은 다 막혀.
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;

COMMIT;

격리 수준 어떻게 고를까?

격리 수준 선택은 네 요구사항에 따라 달라:

  • 속도가 정확성보다 더 중요하고, 팬텀 행 정도는 감수할 수 있다면 Read Committed를 써.
  • 정확성도 중요하지만 성능도 챙기고 싶으면 Repeatable Read를 써.
  • 데이터 정확성이 100% 보장돼야 하고, 속도는 좀 느려도 된다면 Serializable이 딱이야.

조심해! 엄격한 격리 수준은 락이 걸리거나 시스템 성능이 떨어질 수 있어. 합리적인 선택은 성능이랑 데이터 일관성 사이에서 적당히 타협하는 거야.

1
설문조사/퀴즈
트랜잭션 소개, 레벨 39, 레슨 4
사용 불가능
트랜잭션 소개
트랜잭션 소개
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION