[CS/데이터베이스] 트랜잭션과 격리 수준

선우·2025년 12월 25일

CS

목록 보기
12/20

[CS/데이터베이스] 04. 트랜잭션과 격리 수준

⚡ 한 줄 요약: 트랜잭션은 데이터의 무결성을 보장하는 최소 작업 단위이며, ACID 원칙과 격리 수준 설정을 통해 시스템의 성능(동시성)과 데이터의 정합성 사이에서 최적의 균형을 찾는 과정입니다.

1. 👋 들어가며: "왜 내 계좌에서 돈만 빠져나갔을까?"

우리가 쇼핑몰에서 결제 버튼을 누르거나 친구에게 송금할 때, 눈에 보이지 않는 수많은 쿼리가 동시에 움직입니다.

만약 출금은 성공했는데 입금 직전에 서버가 꺼진다면 어떻게 할까요?

이런 끔찍한 상황을 막아주는 최후의 보루가 바로 트랜잭션(Transaction)입니다.

  • 🧐 Why:

    • 단순히 쿼리를 날리는 법을 아는 것을 넘어, 장애 상황에서도 데이터가 안전하게 보호되는 원리를 이해해야 신뢰할 수 있는 서비스를 만들 수 있기 때문입니다.
  • 🎯 Goal:

    • 트랜잭션의 4대 특성(ACID)과 5가지 상태 변화를 이해합니다.
    • 시스템 장애 시 로그를 통해 복구하는 Redo/Undo 메커니즘을 파악합니다.
    • 4단계 격리 수준에 따른 데이터 정합성 문제(Dirty/Phantom Read 등)를 마스터합니다.

📂 2. 트랜잭션

📌 2-1. 데이터베이스의 안전장치, 트랜잭션의 정의와 필요성

트랜잭션은 데이터베이스에서 하나의 논리적 작업 단위를 의미합니다.

우리가 흔히 접하는 '계좌 이체'를 예로 들어볼까요?
이체라는 작업은 사용자 입장에서는 하나의 행위이지만, 실제 DB 내부에서는 여러 개의 쿼리로 나누어 작업이 이루어집니다.

  • 실제 동작 과정: 홍길동의 계좌에서 100만원을 출금하는 쿼리가 먼저 실행되어야 하고, 이후 김철수의 계좌에 100만원을 입금하는 쿼리가 이어서 실행되어야 합니다.

여기서 핵심은 이 두 쿼리가 하나의 논리적인 단위로 묶여야 한다는 것입니다. 그래서 START TRANSACTION 명령어로 이 작업들을 하나로 묶어주는 것이죠.

트랜잭션 내의 모든 작업이 성공해야만 최종적으로 데이터베이스에 반영(COMMIT)되고, 만약 하나라도 실패하면 전체 작업이 취소(ROLLBACK)되어 이전 상태로 돌아갑니다.

이는 데이터의 일관성을 유지하기 위해 필요한 장치입니다.

📌 2-2. 트랜잭션의 4대 필수 특성, ACID

트랜잭션이 안전하게 수행되기 위해서는 네 가지 핵심 특성을 만족해야 하는데, 각 특성의 앞 글자를 따서 ACID라고 부릅니다.

  1. Atomicity (원자성): 트랜잭션 내의 모든 작업은 '모두 성공'하거나 '모두 실패'해야 합니다. 내 계좌에서 돈만 빠지고 상대방 계좌에는 입금되지 않는 상황이 발생하면 안 되기 때문입니다.

  2. Consistency (일관성): 트랜잭션 실행 전후의 데이터베이스 상태는 항상 일관된 규칙을 유지해야 합니다. A가 B에게 5만 원을 보냈다면, 전체 잔고의 총합은 이체 전후가 동일해야 합니다.

  3. Isolation (고립성): 동시에 여러 트랜잭션이 실행될 때, 서로 영향을 주지 않고 독립적으로 처리되어야 합니다. 같은 상품을 두 사람이 동시에 결제할 때, 재고가 하나뿐이라면 순서를 조절하여 한 사람만 성공하도록 보장해야 하는 것과 같습니다.

  4. Durability (지속성): 트랜잭션이 성공적으로 완료(COMMIT)되었다면, 그 결과는 시스템 장애나 전원 차단이 발생하더라도 영구적으로 보존되어야 합니다.

📌 2-3. 트랜잭션의 실제 제어 (COMMIT vs ROLLBACK)

트랜잭션의 흐름은 작업의 성공 여부에 따라 두 갈래로 나뉩니다.

  • 정상 상황 (COMMIT): 모든 SQL문이 정상적으로 완료되면 COMMIT 명령을 실행합니다. 이때 비로소 작업 내용이 데이터베이스에 확정적으로 반영됩니다.

  • 에러 상황 (ROLLBACK): 만약 홍길동의 계좌에서 돈을 뺐는데, '김철수 계좌가 존재하지 않음'과 같은 서버 오류가 발생하면 어떻게 될까요? 이때는 ROLLBACK 명령을 실행하며 이미 수행된 출금 작업까지 모두 취소하고 트랜잭션 시작 전 상태로 되돌립니다.

📌 2-4. 헷갈리기 쉬운 포인트 / 오해 정리

  • 트랜잭션 실행 중에는 데이터가 이미 바뀐 것 아닌가?
    • 커밋하기 전까지 트랜잭션은 실행 중일 뿐, 최종 확정된 상태가 아닙니다. 내부적으로는 연산이 수행되고 있지만, 외부에서 볼 때는 아직 반영되지 않은 상태인 것이죠. 트랜잭션의 ACID 특성을 기억하는 것은 면접뿐만 아니라 안정적인 시스템 설계에서도 매우 중요합니다.

📌 2-5. 한 줄 정리

  • 트랜잭션의 데이터의 무결성을 보장하기 위해 여러 작업을 하나로 묶은 논리적 단위이며, ACID 특성을 통해 '전부 반영되거나 전부 취소됨'을 보장하는 장치입니다.

💻 참고

실무에서는 단순히 STARTCOMMIT을 쓰는 것을 넘어, 프레임워크(예: Spring의 @Transactional)가 이 트랜잭션을 어떻게 관리하는지, 그리고 서비스의 규모에 따라 어느 정도의 격리 수준을 적용할지 결정하는 역량이 중요합니다.

💡 비유로 이해하기

트랜잭션은 '연필로 써놓은 메모'와 같습니다. START TRANSACTION 이후에 적는 내용들은 언제든 지울 수 있는 연필 메모 상태이고, COMMIT 버튼을 눌렀을 때 비로소 그 메모가 '지워지지 않는 잉크'로 변하여 종이에 박히는 것가 같습니다.


📂 3. 트랜잭션 상태

📌 3-1. 트랜잭션의 5가지 상태 흐름

트랜잭션은 실행되는 동안 여러 가지 상태를 거치며 진행됩니다.

일반적으로 아래 5가지 상태를 가집니다:

  1. Active (활성 상태)

    • 트랜잭션이 시작되어 현재 실행 중인 상태입니다.
    • START TRANSACTION; 명령을 날린 직후의 상태로 이해하면 됩니다.
  2. Partially Committed (부분 완료 상태)

    • 트랜잭션의 모든 SQL 문을 실행했지만, 아직 최종적으로 확정(COMMIT)되지 않은 상태입니다.
    • 쿼리는 다 성공했어도 실제 디스크에 기록을 마치기 전 단계입니다.
  3. Committed (완료 상태)

    • 트랜잭션이 성공적으로 완료되어 데이터가 데이터베이스에 영구적으로 반영된 상태입니다.
  4. Failed (실패 상태)

    • 하드웨어 오류나 논리적 에러로 인해 트랜잭션이 정상적으로 완료되지 못한 상태입니다.
  5. Aborted (중단 상태)

    • 트랜잭션이 실패하여 ROLLBACK 연산을 수행해 트랜잭션 시작 전의 상태로 완전히 되돌아간 상태입니다.

📌 3-2. 실제 시나리오로 보는 상태 변화

성공 시나리오 (Committed)

  1. Active

    • 트랜잭션을 시작합니다.
  2. Partially Committed

    • 홍길동 계좌에서 1,000,000원을 출금하고, 김철수 계좌에서 1,000,000원을 입금하는 모든 SQL 실행을 마칩니다.
  3. Committed

    • COMMIT을 실행하여 최종적으로 성공 상태가 됩니다.

실패 시나리오 (Aborted)

  1. Active

    • 트랜잭션을 시작합니다.
  2. Failed

    • 홍길동 계좌에서 출금은 성공했지만, 김철수 계좌가 존재하지 않는 등의 서버 오류가 발생하여 중단됩니다.

3 Aborted

  • ROLLBACK을 실행하여 이미 처리된 출금 작업까지 모두 취소되어 중단 상태가 됩니다.

📌 3-3. 헷갈리기 쉬운 포인트 / 오해 정리

  • 실패(Failed)와 중단(Aborted)의 차이

    • 실패는 문제가 발생하여 멈춘 상태이고, 중단은 롤백을 완료하여 '완전히 정리가 끝난' 상태를 말합니다.
  • 지속성 보장의 시점:

    • 데이터가 절대 사라지지 않는다는 '지속성'은 오직 Committed 상태에 도달했을 때만 보장됩니다.

📌 3-4. 한 줄 정리

  • 트랜잭션은 Active에서 실행을 시작해 쿼리를 모두 마치면 Partially Committed가 되며, 여기서 최종 COMMIT에 성공해야만 Committed(완료) 상태가 되어 데이터가 영구 반영됩니다.

📂 4. 트랜잭션 복구

📌 4-1. 지연 갱신 방식과 Redo

지연 갱신 방식(Deferred Update)은 트랜잭션이 실행되는 동안의 작업 내용을 실제 DB가 아닌 로그(Log)에만 기록하는 방식입니다.

실제 데이터베이스에 반영되는 시점은 오직 COMMIT 명령이 떨어진 이후입니다.

여기서 중요한 포인트가 있습니다.
커밋 명령은 "이 트랜잭션을 확정하겠다"는 선언인데, 이 선언 직후 아주 짧은 찰나에 시스템 장애가 발생할 수 있습니다.

선언은 했지만, 실제로 디스크에 물리적으로 기록되지 못한 상태가 될 수 있는 것이죠.

이런 경우에 대비해 사용하는 것이 바로 Redo(재실행) 연산입니다.

  • 원리

    • 장애 발생 시 로그 파일을 뒤져서 COMMIT은 완료되었지만 실제 DB에 반영되지 못한 내역을 찾아냅니다.
  • 작업

    • 로그에 기록된 새로운 값을 이용해 DB에 다시 반영합니다.
  • 장애 대응

    • 아직 커밋되지 않은 트랜잭션에서 장애가 났다면? DB에는 아무것도 안 써졌으니 그냥 무시하면 그만입니다.

📌 4-2. 즉시 갱신 방식과 Undo

반대로 즉시 갱신 방식(Immediate Update)은 쿼리가 실행될 때마다 DB에 바로바로 반영하는 방식입니다.

주로 일부 오래된 시스템에서 사용하는 방식이죠.

이 방식은 장애가 났을 때 처리가 조금 더 복잡합니다.

  • 문제

    • 트랜잭션 도중에 장애가 나면, 일부 작업은 이미 DB에 써져버린 상태일 수 있습니다.
  • 해결 (Undo)

    • 커밋되지 않았는데 DB에 반영된 작업들을 되돌려야 합니다.
    • 로그에 저장된 변경 전 이전 값을 사용하여 데이터를 원래대로 복구하는 Undo(취소) 연산을 수행합니다.

📌 4-3. 헷갈리기 쉬운 포인트 / 오해 정리

  • 지연 갱신 방식에서도 Undo가 필요한가요?

    • 아닙니다. 지연 갱신 방식은 커밋 전에는 DB를 건드리지 않기 때문에 무를 작업(Undo) 자체가 없습니다. 오직 Redo만 고민하면 됩니다.
  • 로그에는 무엇이 적혀 있나요?

    • 지연 갱신 로그에는 쿼리 실행 결과로 반영될 '새로운 값'들이 들어있고, 즉시 갱신 방식의 Undo 로그에는 복구를 위한 '이전 값'이 기록됩니다.

📌 4-4. 한 줄 정리

  • 트랜잭션 복구의 핵심은 로그를 활용하는 것이며, 커밋된 내역은 Redo로 살려내고, 커밋되지 않은 내역은 Undo로 되돌려 데이터의 원자성을 보장합니다.

💻 참고

실무에서 로그 파일(Write-Ahead Log, WAL)의 중요성은 아무리 강조해도 지나치지 않습니다. 우리가 흔히 쓰는 MySQL의 InnoDB 엔진도 이 WAL 메커니즘을 사용하여 성능과 안전성을 동시에 잡습니다.

"데이터는 로그에 먼저 써지고, 나중에 DB 파일에 써진다"는 대원칙을 이해하고 있다면, 분산 시스템이나 클라우드 환경의 데이터 복구 로직도 쉽게 파악할 수 있습니다.


📂 5. 트랜잭션 격리 수준

📌 5-1. 격리 수준의 정의와 동시성 제어

하나의 트랜잭션 내에서 작업 중인 데이터에 대해 다른 트랜잭션의 접근 범위를 결정하는 설정입니다.

쉽게 말해, 내가 어떤 데이터를 고치고 있을 때 남이 그 데이터를 어느 정도까지 볼 수 있게 할 것인가를 정하는 기준입니다.

이 설정은 정확성동시성 사이의 줄타기와 같습니다.

  • 격리 수준이 낮다

    • 여러 트랜잭션을 동시에 실행시킬 수 있어 처리 효율(동시성)이 좋아지지만, 데이터의 정확성은 떨어질 수 있습니다.
  • 격리 수준이 높다

    • 데이터의 정확성은 완벽히 보장되지만, 트랜잭션들이 서로 줄을 서서 기다려야 하므로 동시성이 떨어지고 시스템 성능이 저하될 수 있습니다.

가장 낮은 격리 수준인 READ UNCOMMITTED부터 가장 높은 수준인 SERIALIZABLE까지 총 4단계가 존재합니다.

📌 5-2. 격리 수준 4단계와 발생 가능한 문제

표준 SQL에서 정의하는 격리 수준은 아래와 같으며, 실무에서 사용하는 대부분의 DBMS(MySQL, PostgreSQL 등)는 REPEATABLE READREAD COMMITTED를 기본값으로 채택하고 있습니다.

격리 수준설명문제 발생 가능성
READ UNCOMMITTED커밋되지 않은 데이터도 읽기 허용 (가장 낮음)Dirty Read 발생 가능
READ COMMITTED커밋된 데이터만 읽기 허용Non-repeatable Read 발생 가능
REPEATABLE READ트랜잭션 내 동일 데이터 조회 시 항상 같은 값 반환Phantom Read 발생 가능
SERIALIZABLE트랜잭션을 순차적으로 실행 (가장 높음/엄격)성능 저하 우려

📌 5-3. 헷갈리기 쉬운 포인트 / 오해 정리

  • SERIALIZABLE은 모든 문제를 해결한다?

    • 데이터 정합성 측면에서는 맞지만, 실무에서는 거의 사용되지 않습니다.
    • 트랜잭션을 직렬화하여 실행하기 때문에 사실상 '멀티태스킹'이 안 되는 DB가 되어버리기 때문입니다.
  • 낮은 격리 수준은 무조건 동시성이 좋은가?

    • 동시성(여러 트랜잭션 동시 실행)은 좋아지지만, 그만큼 데이터가 꼬여서 발생하는 비즈니스 로직상의 에러를 개발자가 직접 코드로 방어해야 하는 비용이 발생할 수 있습니다.

📌 5-4. 한 줄 정리

  • 격리 수준은 데이터 정확성과 시스템 성능 사이의 균형을 맞추는 설정이며, READ UNCOMMITTED가 가장 낮고 SERIALIZABLE이 가장 높은 단계입니다.

💡 비유로 이해하기

격리 수준은 도서관 열람실의 개인 공간과 같습니다.

칸막이가 아예 없으면(낮으면) 옆 사람과 대화하며 정보를 빨리 주고받을 수 있지만 방해를 받기 쉽고, 완전히 폐쇄된 1인실(높으면)은 집중도는 완벽하지만 공간 활용 효율(동시성)이 떨어지는 것과 같습니다.


📂 6. 트랜잭션 격리 수준 (1) - READ UNCOMMITTED

📌 6-1. READ UNCOMMITTED와 Dirty Read

다른 트랜잭션이 커밋하지 않은 데이터를 읽을 수 있게 허용하는 가장 낮은 격리 수준입니다.

이 단계의 핵심 특징은 특정 트랜잭션에서 변경한 내용이 아직 확정(COMMIT)되지 않았더라도 다른 트랜잭션이 그 변경된 값을 바로 볼 수 있다는 점입니다.

구체적인 시나리오를 통해 데이터 정합성이 어떻게 깨지는지 살펴보겠습니다.

  1. 트랜잭션 A (수정 중)

    • 홍길동의 잔액을 0원으로 수정하는 쿼리를 실행합니다.
      UPDATE accounts SET balance = 0 WHERE name = '홍길동';
    • 하지만 아직 커밋은 하지 않은 상태입니다.
  2. 트랜잭션 B (조회 중)

    • 트랜잭션 A가 커밋되지 않았음에도 불구하고, 이 격리 수준에서는 쿼리가 실행된 결과를 그대로 읽어옵니다.
  3. 결과

    • 트랜잭션 B가 읽은 홍길동의 잔액은 0원이 됩니다.
  4. 반전(ROLLBACK)

    • 만약 트랜잭션 A에서 문제가 생겨 ROLLBACK을 하면, 홍길동의 잔액은 수정 전 원래 금액으로 남게 됩니다.
  5. 문제 발생

    • 하지만 트랜잭션 B는 이미 홍길동의 잔액을 0원으로 읽어버린 후입니다.
    • 존재하지도 않는 '유령' 같은 데이터를 읽게 된 것이죠.

이처럼 다른 트랜잭션이 커밋하지 않은 데이터를 읽는 현상을 더티 리드(Dirty Read)라고 하며, 이는 데이터의 정합성에 심각한 문제를 야기합니다.

📌 6-2. 헷갈리기 쉬운 포인트 / 오해 정리

  • 쿼리가 실행되면 무조건 DB에 반영되는 것 아닌가요?

    • 아닙니다. 트랜잭션 내부에서 쿼리가 성공적으로 실행되었다 하더라도, 최종적으로 COMMIT을 하기 전까지는 '확정'된 상태가 아닙니다.
    • READ UNCOMMITTED는 이 '확정되지 않은 임시 결과'를 남에게 보여주는 것입니다.
  • 롤백하면 읽어간 데이터도 취소되나요?

    • 아닙니다. 이미 다른 트랜잭션이 읽어간 데이터는 취소할 수 없습니다. 이것이 정합성 문제의 핵심입니다.

📌 6-3. 한 줄 정리

  • READ UNCOMMITTED는 커밋되지 않은 데이터를 읽는 Dirty Read를 허용하여 성능은 매우 빠르지만 데이터 정합성은 보장하지 못하는 격리 수준입니다.

📂 7. 트랜잭션 격리 수준 (2) - READ COMMITTED

📌 7-1. READ COMMITTED와 Non-Repeatable Read

커밋 완료된 데이터만 읽을 수 있게 허용하여 Dirty Read를 방지하는 격리 수준입니다.

1단계(READ UNCOMMITTED)의 치명적인 약점이었던 Dirty Read를 해결하기 위해, 오직 커밋된 데이터만 읽도록 제한합니다.

하지만 이 단계에서도 Non-Repeatable Read라는 정합성 문제가 여전히 존재합니다.

아래 시나리오를 통해 트랜잭션 B의 입장에서 어떤 일이 벌어지는지 살펴봅시다.

  1. 트랜잭션 B 시작

    • 홍길동의 잔액을 조회합니다.
    • 기존 값인 50만원이 나옵니다.
  2. 트랜잭션 A 개입

    • 동시에 실행 중인 트랜잭션 A가 홍길동의 잔액을 0원으로 수정(UPDATE)합니다.
  3. 트랜잭션 B 재조회(중간 단계)

    • 아직 트랜잭션 A가 커밋 전이라면, 트랜잭션 B는 여전히 기존 값인 50만원을 읽습니다.
    • 이로써 Dirty Read가 방지됩니다.
  4. 트랜잭션 A 커밋

    • 트랜잭션 A가 수정을 확정 짓습니다.
  5. 트랜잭션 B 최종 재조회

    • 트랜잭션 B가 동일한 쿼리로 다시 조회하자, 이번에는 0원이라는 결과가 나옵니다.

결과적으로 트랜잭션 B 입장에서는 동일한 트랜잭션 내에서 똑같은 쿼리를 실행했는데 조회 결과가 달라지는 현상을 겪게 됩니다.

이를 Non-Repeatable Read라고 부릅니다.

📌 7-2. 헷갈리기 쉬운 포인트 / 오해 정리

  • 커밋된 것만 읽으니까 안전한 것 아닌가요?

    • 물론 Dirty Read보다는 안전하지만, '한 트랜잭션 안에서는 데이터가 변하지 않아야 한다'는 기준에서는 여전히 불안정합니다.
    • 트랜잭션 B가 끝날 때까지 데이터가 고정되지 않기 때문입니다.
  • Dirty Read vs Non-Repeatable Read

    • 커밋 안 된 걸 읽으면 Dirty Read, 읽는 도중에 남이 커밋해서 값이 변하면 Non-Repeatable Read입니다.

📌 7-3. 한 줄 정리

  • READ COMMITTED는 커밋된 데이터만 읽어 Dirty Read는 막지만, 동일 트랜잭션 내에서 조회 결과가 달라지는 Non-Repeatable Read가 발생할 수 있는 격리 수준입니다.

💻 참고

Oracle이나 PostgreSQL 같은 주요 RDBMS가 이 READ COMMITTED를 기본 격리 수준으로 채택하고 있습니다.

웹 애플리케이션의 일반적인 비즈니스 로직에서는 큰 문제가 되지 않는 경우가 많지만, 데이터의 엄격한 일관성이 필요한 금융권 통계나 정산 로직에서는 이 Non-Repeatable Read을 방어하기 위해 격리 수준을 높이거나 명시적인 락(Lock)을 활용하기도 합니다.

💡 비유로 이해하기

식당 메뉴판을 보고 있는데, 내가 메뉴를 고르는 사이 주방장이 메뉴판의 가격을 수정하고 커밋(확정)한 상황입니다.

처음 메뉴판을 봤을 땐 1만 원이었는데, 주문하려고 다시 보니 1.2만원으로 변해 있는 것과 같습니다.


📂 8. 트랜잭션 격리 수준 (3) - REPEATABLE READ

📌 8-1. "한 번 본 데이터는 끝까지 간다" - REPEATABLE READ

트랜잭션 내에서 동일한 데이터를 조회했을 때 항상 같은 값이 반환됨을 보장하는 격리 수준입니다.

2단계(READ COMMITTED)에서는 조회 도중 남이 커밋을 하면 값이 바뀌는 현상이 있었죠.

이를 해결하기 위해 3단계인 REPEATABLE READ는 트랜잭션이 시작되기 전에 이미 커밋된 내용만 조회하는 방식을 취합니다.

가장 큰 특징은 트랜잭션 B의 커밋이 트랜잭션 A의 시작보다 나중에 실행되었지만, 트랜잭션 A는 데이터 조회 시 그 커밋 내용을 절대 반영하지 않는다는 점입니다.

즉, 트랜잭션 A는 자신이 시작된 시점의 '데이터 스냅샷'을 끝까지 고수합니다.

📌 8-2. 내부 동작 원리와 락(Lock)의 한계

기본적으로 데이터베이스는 처음 SELECT 했던 행에 대해 내부적으로 락(Lock)을 걸어둡니다.

이를 통해 내가 읽고 있는 동안 다른 트랜잭션이 그 행을 수정하거나 삭제하지 못하도록 물리적으로 막아버리는 것이죠.

하지만 여기서 치명적인 빈틈이 하나 생깁니다.
바로 새로운 행의 삽입(INSERT)은 막지 못한다는 점입니다.

이유는 단순합니다.
아직 존재하지 않는 행에는 락을 걸 수 없기 때문입니다.

이 때문에 발생하는 현상이 바로 팬텀 리드(Phantom Read)입니다.

기존 데이터는 락 덕분에 변하지 않지만, 새로운 데이터가 삽입되면서 마치 유령(Phantom)처럼 이전에 없던 데이터가 조회 결과에 나타나게 됩니다.

📌 8-3. 헷갈리기 쉬운 포인트 / 오해 정리

  • 락을 걸면 INSERT도 막히는 거 아닌가요?

    • 오해입니다. 락은 '존재하는 데이터'를 보호하는 도구입니다.
    • 아직 태어나지도 않은(삽입되지 않은) 데이터에 미리 자물쇠를 채울 수는 없기 때문에 신규 삽입에 의한 정합성 이슈는 여전히 남습니다.
  • 트랜잭션이 길어지면 어떻게 되나요?

    • REPEATABLE READ를 유지하기 위해 이전 버전의 데이터를 계속 보관해야 하므로 시스템 부하가 커질 수 있습니다.

📌 8-4. 한 줄 정리

  • 트랜잭션 시작 시점의 스냅샷을 사용하여 '반복 조회 시 일관성'은 보장하지만, 신규 행 삽입으로 인한 '팬텀 리드'는 허용하는 격리 수준입니다.

💻 참고

우리가 실무에서 가장 많이 쓰는 MySQL(InnoDB 엔진)의 기본 격리 수준이 바로 이 REPEATABLE READ입니다.

흥미로운 점은 InnoDB는 Next-Key Lock이라는 기술을 써서 이 단계에서도 팬텀 리드를 어느 정도 방어해 준다는 사실이죠.


📂 9. 트랜잭션 격리 수준 (4) - SERIALIZABLE

📌 9-1. "줄을 서시오" - SERIALIZABLE의 동작 원리

모든 정합성 문제를 방지하는 가장 강력한 격리 수준으로, 각 트랜잭션이 마치 하나씩 순서대로 실행되는 것처럼 동작합니다.

4단계인 SERIALIZABLE은 가장 엄격한 수준입니다.

앞선 단계들에서 발생했던 Dirty Read, Non-Repeatable Read, Phantom Read를 모두 완벽하게 방지하죠.

하지만 트랜잭션을 직렬화하여 처리하기 때문에 성능은 가장 느립니다.

📌 9-2. 헷갈리기 쉬운 포인트 / 오해 정리

  • 가장 높은 수준이 무조건 정답이다?
    • 아닙니다. SERIALIZABLED은 트랜잭션 간의 간섭을 완전히 차단하기 위해 많은 비용(Locking 등)을 소모합니다.
    • 서비스의 특성에 맞춰 Trade-off를 고려하는 것이 시니어의 역량입니다.
  • 격리 수준이 높으면 무조건 느린가요?
    • 이론적으로는 그렇습니다. 하지만 데이터가 적거나 충돌 가능성이 낮은 환경에서는 체감이 적을 수 있습니다.
    • 그러나 트래픽이 몰리는 시점에서는 SERIALIZABLE이 병목 현상의 주범이 됩니다.

📌 9-3. 한 줄 정리

  • SERIALIZABLE은 데이터 정합성을 완벽히 보장하지만 성능 저하가 크기 때문에, 금융권 등 정밀함이 최우선인 특수 상황에서만 제한적으로 사용됩니다.

🎁 10. 정리

🔑 요약

  • 안전의 기반, ACID

    • 하나라도 실패하면 다 취소하는 원자성(Atomicity)부터 장애 후에도 데이터가 남는 지속성(Durability)까지, 이 네 가지 원칙은 모든 금융/커머스 로직의 근간이 됩니다.
  • 상태와 복구의 마법

    • 트랜잭션은 단순히 실행 중인 상태를 넘어 Partially Committed라는 신중한 단계를 거칩니다.
    • 장애가 발생해도 Redo(재실행)로 살려내고, Undo(취소)로 되돌리는 로그 기반 복구 메커니즘이 있기에 우리는 안심하고 데이터를 다룰 수 있습니다.
  • 격리 수준의 트레이드오프

    • 정확성을 높이면 성능이 떨어지고, 성능을 높이면 데이터가 꼬일 위험이 커집니다.
    • READ COMMITTEDREPEATABLE READ 중 무엇을 선택할지는 서비스의 트래픽과 데이터의 중요도에 따라 결정해야 하는 고도의 엔지니어링 의사결정입니다.
  • 현대적 DB의 지혜

    • SERIALIZABLE이 가장 안전하지만 실무에선 거의 쓰지 않습니다.
    • 대신 우리가 흔히 쓰는 MySQL(InnoDB)처럼 REPEATABLE READ에서도 기술적(Next-Key Lock)으로 팬텀 리드를 막아주는 엔진의 특성을 이해하는 것이 중요합니다.

0개의 댓글