7장. 서로를 방해하지 않는 척 — 격리성

만화로 보는 요약 — 먼저 읽어보세요
동시에 같은 데이터를 읽고 쓰면 오버부킹이 생긴다 → 격리성은 혼자 실행한 것처럼 보이게 한다 → 완벽할수록 안전하지만 느려져 격리 수준으로 타협한다.
면접 실전 질문: ① 격리성이 막는 동시성 사고 ② 직렬성이 안전한 기준인 이유 ③ 격리 수준이 안전과 속도의 다이얼인 이유
배경 — 나 혼자면 쉽다
지금까지 우리는 트랜잭션 하나만 놓고 이야기했어요. 그런데 현실의 DB에는 수천 개의 트랜잭션이 동시에 쏟아집니다. 여기서 모든 게 어려워져요.
혼자 쓰면 아무 문제 없습니다. 잔고를 읽고, 계산하고, 쓰면 끝. 하지만 옆에서 누군가 같은 데이터를 동시에 건드리는 순간, 2장에서 봤던 그 사고 — 여럿이 같은 값을 동시에 읽고-고치고-쓰는 경쟁(흔히 lost update라 부릅니다) — 가 돌아옵니다. 그래서 트랜잭션에는 세 번째 약속이 필요해요. “동시에 돌아가도, 마치 나 혼자 쓰는 것처럼 느끼게 해줘.” 이게 ACID의 I, 격리성(Isolation) 입니다.
스토리 — 마지막 한 자리
콘서트 티켓, 마지막 좌석 하나가 남았습니다. 그런데 하필 두 사람이 같은 순간에 결제 버튼을 눌렀어요. 각자의 트랜잭션은 이렇게 돌아갑니다.
[민수] SELECT 남은 좌석 → 1개! (가드 통과) 예약 진행
[영희] SELECT 남은 좌석 → 1개! (가드 통과) 예약 진행 ← 민수가 아직 COMMIT 전이라, 여전히 1로 보임
[민수] UPDATE 좌석 = 좌석 - 1 → 0, 티켓 발급, COMMIT
[영희] UPDATE 좌석 = 좌석 - 1 → -1, 티켓 발급 ← 이미 0인데 또 깎았다! 없는 좌석을 팔았다포인트는 두 단계로 나뉜다는 거예요. 확인(SELECT 가드) 과 차감(UPDATE) 이
따로 놉니다. 둘 다 “1개”라는 낡은 확인값을 믿고 통과했고, 영희의 차감은 민수가
0으로 만든 현재값에 적용돼 -1이 됩니다. 좌석은 -1, 오버부킹이 터졌어요. 두 사람이 한 명씩 순서대로 왔다면 절대 안 생길 일이,
동시에 왔다는 이유 하나로 벌어진 겁니다.
문제의 뿌리는 영희가 민수의 아직 안 끝난 작업을 못 봤다는 거예요. 각자 자기만의 세계에 있는 척했지만, 사실은 같은 좌석을 밟고 있었던 거죠. 격리성이 지키려는 게 바로 이겁니다. “네가 하는 동안, 남이 중간에 끼어든 걸 보지 않게 해줄게.”
핵심 — “혼자인 척”의 이상과 대가
격리성의 이상적인 목표는 3장에서 만난 그 단어, 직렬성(serializability) 입니다. 다시 떠올려 볼게요.
동시에 실행한 결과가, 그것들을 어떤 순서로든 하나씩 실행한 결과와 같다면 — 안전하다.
민수와 영희가 진짜로 한 명씩 처리됐다면 오버부킹은 없었어요. 완벽한 격리성은 “동시에 돌려도 결과는 한 명씩 한 것과 똑같다”를 보장합니다. 마치 모두가 각자 유리 부스 안에서 혼자 일하는 것처럼요.
그런데 여기서 이 장의 진짜 긴장이 등장합니다. 완벽한 격리는 느립니다.
생각해보면 당연해요. “남이 중간에 못 끼어들게” 하려면 순서를 강제해야 하고 — 가장 단순한 방법은 데이터를 잠그는 것이죠. 그럼 다른 사람은 기다려야 합니다. 모두를 완벽하게 한 줄로 세우면 안전하지만, 동시성(여러 명이 동시에 빠르게 처리되는 것)은 사라집니다. 안전과 속도가 정면으로 부딪치는 거예요.
그래서 현실의 DB는 타협합니다. “얼마나 격리할지”를 단계로 나눠서 고를 수 있게 해뒀어요. 조금 덜 격리하는 대신 빠르게, 아니면 완벽하게 격리하는 대신 느리게. 이 단계를 격리 수준(isolation level) 이라 부릅니다. Spring에서도 이렇게 고르죠.
@Transactional(isolation = Isolation.SERIALIZABLE) // 가장 안전, 가장 느림
public void reserve(long concertId) { ... }그리고 격리를 느슨하게 풀 때마다, 2장의 lost update 같은 괴상한 현상들이 슬금슬금 고개를 듭니다. 어떤 걸 허용하고 어떤 걸 막을지가 곧 격리 수준의 정체예요.
여기까지가 격리성의 큰 그림입니다. 사실 이 글자 하나가 너무 깊어서, 이 책은 제3부 전체를 격리성에 바칩니다. 동시성이 부르는 괴현상들(9장), 네 개의 격리 수준(10장), 표준과 현실의 간극(11장)까지요. 지금은 딱 하나만 챙기세요. 격리성은 “안전 ↔ 속도”의 트레이드오프이고, 그걸 조절하는 다이얼이 격리 수준이다.
정리
- 격리성(Isolation) = 여러 트랜잭션이 동시에 돌아가도 “나 혼자 쓰는 것처럼” 느끼게 하는 약속. 안 지키면 오버부킹·lost update 같은 사고가 터진다.
- 이상적 목표는 직렬성(동시 실행 = 순차 실행 결과). 하지만 완벽한 격리는 느리다.
- 그래서 DB는 안전 ↔ 속도를 격리 수준(isolation level) 이라는 다이얼로 타협한다. 느슨하게 풀수록 빠르지만 괴현상이 새어 나온다.
- 격리성은 너무 깊어 제3부 전체가 이 주제다(9~11장).
생각해볼 질문: 격리성은 트랜잭션이 돌아가는 동안의 안전을 지킵니다. 그런데
COMMIT으로 무사히 끝냈다 해도, 바로 다음 순간 서버 전원이 나가면 그 결과는
살아남을까요? ACID의 마지막 글자, D를 매듭짓고 제3부로 갑니다.