443. 고립성 (Isolation)과 병행 제어

⚠️ 이 문서는 수만 명의 사용자가 0.01초 차이로 동시에 같은 데이터를 수정하려고 달려들 때, **"내가 작업하는 동안 아무도 내 데이터를 건드리지 마!"라며 자물쇠(Lock)를 걸어 서로의 트랜잭션이 철저하게 독립적으로 실행되도록 보호하는 '고립성'**을 다룹니다.

핵심 인사이트 (3줄 요약)

  1. 본질: 고립성은 여러 트랜잭션이 동시에 겹쳐서 실행되더라도, 마치 '자기 혼자만 순서대로 실행되는 것처럼(직렬화)' 느끼게 만들어주는 데이터베이스의 마법이다.
  2. 보장 기법: 병행 제어(Concurrency Control). 데이터에 자물쇠(Locking)를 걸거나 버전(MVCC)을 나눠서, 남이 작업 중인 임시 데이터를 몰래 훔쳐보지 못하게 막는다.
  3. 격리 수준 (Isolation Level): 완벽하게 문을 걸어 잠그면(Serializable) 데이터는 안전하지만 대기 줄이 길어져서 서버가 느려진다. 그래서 실무에서는 문을 살짝 열어두는(Read Committed 등) 타협안을 쓴다.

Ⅰ. 개요: 화장실 문은 잠그고 써라 (Context & Necessity)

내 통장에 10만 원이 있다.

  • T1 (나): 통장에서 5만 원을 출금 중이다. (진행 중: 통장 잔고 5만 원)
  • T2 (엄마): 동시에 내 통장에 10만 원을 입금 중이다. (T1이 작업 중인 5만 원을 보고, 거기에 10만 원을 더해서 15만 원으로 업데이트함)

그런데 갑자기 내가 출금을 취소(ROLLBACK)했다.

  • 내 원래 잔고는 10만 원이었고, 엄마가 10만 원을 보냈으니 잔고는 20만 원이 되어야 맞다.
  • 하지만 엄마(T2)는 내가 롤백하기 전의 임시 데이터(5만 원)를 훔쳐보고 계산했기 때문에, 최종 잔고는 15만 원이 되어버린다. (5만 원 증발!)

이런 끔찍한 간섭을 막기 위해, 내가 화장실에 들어가면(트랜잭션 시작) 밖에서 엄마가 문을 못 열게 **자물쇠(Lock)**를 걸어버리는 것이 바로 **고립성(Isolation)**이다.

📢 섹션 요약 비유: 고립성은 **'시험 칠 때 커튼 치기'**와 같습니다. 친구가 아직 답을 썼다 지웠다 고민 중인데(트랜잭션 진행 중), 내가 그 임시 답안을 훔쳐보고 내 답안지에 베껴 적으면(오염된 읽기) 결국 둘 다 시험을 망치게 됩니다. 그래서 시험 칠 때는 서로의 답안지를 절대 못 보게 커튼(Lock)을 쳐야 합니다.


Ⅱ. 고립성을 깨뜨리는 3가지 이상 현상 ★

자물쇠를 헐겁게 잠그면 해커처럼 비집고 들어와서 훔쳐보는 현상들이다. (격리 수준과 짝지어 출제됨)

  1. Dirty Read (오염된 읽기)
    • 남이 아직 COMMIT도 안 한 '임시 데이터'를 훔쳐 읽는 현상. (가장 최악의 버그)
  2. Non-Repeatable Read (반복 불가능한 읽기)
    • 내가 트랜잭션 안에서 A 데이터를 두 번 읽었는데, 그사이에 남이 A 값을 UPDATE 해버려서 두 번의 결과값이 달라지는 현상.
  3. Phantom Read (유령 읽기)
    • 내가 트랜잭션 안에서 A 테이블의 총개수를 두 번 셌는데, 그사이에 남이 데이터를 INSERT 해버려서 없던 데이터(유령)가 뿅 하고 나타나 개수가 달라지는 현상.

Ⅲ. 4단계 격리 수준 (Isolation Level)

고립성을 완벽하게 지키려면 문을 꽉 잠그면 된다. 하지만 그러면 밖에서 대기하는 손님들이 화를 낸다(성능 저하). 그래서 DB 엔진은 문을 잠그는 강도를 4단계로 나누어 제공한다.

  1. Read Uncommitted (커밋 안 된 것도 읽기)
    • 문을 아예 안 잠근다. 남이 쓰고 있는 데이터(Dirty Read)도 막 훔쳐본다. (속도는 제일 빠르나, 데이터가 다 꼬임. 실무 절대 사용 불가)
  2. Read Committed (커밋된 것만 읽기) ★ (오라클 기본값)
    • COMMIT이 끝난 확정 데이터만 읽는다. (Dirty Read 방어 성공)
    • 하지만 내가 읽는 동안 남이 값을 바꿔버릴 순 있다. (Non-Repeatable Read 발생)
  3. Repeatable Read (반복해서 읽기) ★ (MySQL 기본값)
    • 내가 트랜잭션을 시작하면, 내 전용 스냅샷(MVCC)을 찍어둬서 남이 값을 바꿔도 나는 내 스냅샷만 본다. (Non-Repeatable Read 방어 성공)
    • 하지만 남이 새로 집어넣은 데이터(INSERT)는 보일 수 있다. (Phantom Read 발생)
  4. Serializable (직렬화 - 완벽 격리)
    • 완벽하게 문을 잠근다. 내가 테이블을 보는 동안 남들은 그 테이블에 INSERT조차 할 수 없다. (Phantom Read 방어 성공)
    • 문제점: 성능이 지옥 끝까지 떨어져서 실무에서는 절대 쓰지 않는다.
┌──────────────────────────────────────────────────────────────┐
│           트랜잭션 격리 수준(Isolation Level) 방어 능력 요약표         │
├──────────────────────────────────────────────────────────────┤
│ 격리 수준 (강도)       │ Dirty Read │ Non-Repeatable Read │ Phantom Read │
│ ────────────────────┼────────────┼─────────────────────┼──────────────│
│ Read Uncommitted   │   발생 ❌   │       발생 ❌        │    발생 ❌   │
│ Read Committed     │   방어 🛡️   │       발생 ❌        │    발생 ❌   │
│ Repeatable Read    │   방어 🛡️   │       방어 🛡️        │    발생 ❌   │
│ Serializable       │   방어 🛡️   │       방어 🛡️        │    방어 🛡️   │
│                                                              │
│ ★ 실무 팁: MySQL(InnoDB)은 Repeatable Read를 기본으로 쓰면서도,      │
│          자체적인 갭 락(Gap Lock) 기술을 통해 Phantom Read까지 방어해 줌!│
└──────────────────────────────────────────────────────────────┘

Ⅳ. 결론

"데이터의 무결성과 시스템의 속도 사이, 가장 위대한 줄타기." 고립성(Isolation)은 ACID 4대 특성 중 유일하게 '100% 완벽함을 포기하고 타협하는' 특성이다. 만약 고립성을 100% 지키려면 모든 트랜잭션을 1열로 세워놓고 순서대로(Serializable) 처리해야 하는데, 그러면 수강 신청 날 대학교 서버는 1초 만에 다운될 것이다. 따라서 DBA와 백엔드 개발자는 비즈니스 로직의 성격(돈이 오가는가, 아니면 단순 조회인가)에 따라 격리 수준(Isolation Level)을 섬세하게 조율하여, 약간의 읽기 오류(Phantom Read)를 감수하더라도 동시성(Concurrency) 성능을 챙기는 결단을 내려야 한다.


📌 관련 개념 맵

  • 관련 특성: 원자성(A), 일관성(C), 영속성(D) (440번 문서)
  • 제어 기술: Concurrency Control (병행 제어)
  • 잠금 기술: Lock (Shared Lock, Exclusive Lock), MVCC (다중 버전 동시성 제어)
  • 데드락 (Deadlock): 서로 자물쇠를 걸고 무한 대기하는 현상

👶 어린이를 위한 3줄 비유 설명

  1. 내가 일기장을 쓰고 있는데(트랜잭션 진행 중), 동생이 내 등 뒤에서 일기장을 몰래 훔쳐보고 엄마한테 이르면 안 되겠죠? (Dirty Read)
  2. 고립성은 내가 일기를 다 쓰고 책상 서랍에 완벽하게 집어넣을 때까지(Commit), 내 방 문을 찰칵 잠가서(Lock) 아무도 못 들어오게 막는 거예요.
  3. 근데 문을 너무 꽉 잠가두면(Serializable) 엄마가 청소하러 들어올 수가 없으니까, 적당히 문을 반쯤 열어두는 타협안(격리 수준)을 쓰는 거랍니다!