Skip to content

Commit 924ac0a

Browse files
committed
Update DB Post " [DB] 트랜잭션 격리 수준 "
1 parent 38fd346 commit 924ac0a

File tree

1 file changed

+52
-1
lines changed

1 file changed

+52
-1
lines changed

_posts/2023-02-20-DB-Transaction-Isolation-Level.md

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,38 @@ author: devFancy
147147

148148
* Phantom Read
149149

150+
#### REPEATABLE READ와 스냅샷
151+
152+
> 2025.07.23 업데이트
153+
154+
앞선 설명만 보면 한 가지 의문이 생길 수 있다.
155+
156+
"REPEATABLE READ도 커밋된 데이터를 읽는다고 했는데, 왜 다른 트랜잭션이 커밋한 최신 데이터를 보지 못하는 걸까?"
157+
158+
앞선 설명만 보면 한 가지 의문이 생길 수 있습니다. "REPEATABLE READ도 커밋된 데이터를 읽는다고 했는데, 왜 다른 트랜잭션이 커밋한 최신 데이터를 보지 못하는 걸까?"
159+
160+
결론부터 말하면 **`REPEATABLE READ`는 트랜잭션 내 첫 `SELECT` 시점의 '스냅샷(Snapshot)'을 만들어, 트랜잭션이 끝날 때까지 그 스냅샷의 데이터만 일관되게 보여주기 때문**이다. 이는 MySQL의 **MVCC(Multi-Version Concurrency Control)** 와 관련된 핵심 동작 방식이다.
161+
162+
다른 트랜잭션이 데이터를 변경하고 커밋해도, 현재 트랜잭션은 **'반복 가능한 읽기'를 보장하기 위해 의도적으로 최신 변경 사항을 무시**하고 자신만의 스냅샷을 참조한다.
163+
164+
아래 그림은 동시 결제 요청 시 스냅샷으로 인해 문제가 발생하는 과정을 보여준다.
165+
166+
> REPEATABLE READ의 스냅샷 동작 원리
167+
168+
![]()
169+
170+
시나리오 분석:
171+
172+
1. 스냅샷 생성: 트랜잭션 B가 처음 잔액을 조회(`SELECT`)하는 순간, InnoDB는 해당 시점의 데이터 복사본, 즉 **스냅샷**을 만듭니다. 이 스냅샷에는 잔액이 `10,000원`으로 기록된다.
173+
2. 데이터 변경: 트랜잭션 A가 먼저 Lock을 얻어 결제를 처리하고, 실제 DB의 잔액을 `5,000원`으로 변경 후 커밋한다.
174+
3. 스냅샷 참조: 트랜잭션 B는 A의 커밋 이후 로직을 재개하지만, DB의 최신 데이터를 보지 않고 **자신이 만들어 둔 스냅샷을 계속 참조한**다. 따라서 B에게 잔액은 여전히 `10,000원` 이다.
175+
4. 업데이트 실패: 결국 B는 `UPDATE ... WHERE balance = 10000` 쿼리를 실행하게 되고, 실제 DB 값(5,000원)과 달라 업데이트는 실패한다.
176+
177+
이처럼 `REPEATABLE READ`**'트랜잭션 내의 일관성'** 을 위해 **'데이터의 최신성'** 을 희생하는 전략을 사용한다.
178+
179+
이 특징을 이해해야 동시성 이슈가 발생했을 때 정확한 원인을 파악하고 `READ COMMITTED`와 같은 다른 격리 수준을 대안으로 고려할 수 있다.
180+
181+
150182
### SERIALIZABLE
151183

152184
* 말 그대로 트랜잭션을 순차적으로 진행시키는 것을 말한다.(직렬화)
@@ -161,6 +193,23 @@ author: devFancy
161193
162194
* 발생할 수 있는 문제 X
163195

196+
### 어떤 격리 수준을 선택해야 할까?
197+
198+
지금까지의 내용을 보면 `REPEATABLE READ`의 스냅샷 동작 방식이 문제를 일으켰으니, 무조건 `READ COMMITTED`를 써야 할 것처럼 보일 수 있다. **하지만 항상 그런 것은 아니다.**
199+
200+
**`REPEATABLE READ`가 유리한 경우도 있다.**
201+
예를 들어, 사용자가 쇼핑몰에서 1,000원짜리 상품을 보고 결제를 진행한다고 상상해 보자. 만약 격리 수준이 `READ COMMITTED`라면, 사용자가 결제하는 그 짧은 순간에 운영자가 상품 가격을 1,500원으로 변경하고 커밋할 수 있다. 이 경우 사용자는 1,000원을 예상했지만 실제로는 1,500원이 결제되는 혼란스러운 상황을 겪게 된다.
202+
203+
반면, `REPEATABLE READ` 환경에서는 사용자의 결제 트랜잭션이 시작될 때 상품 가격(1,000원)이 스냅샷으로 저장된다. 중간에 가격이 1,500원으로 변경되더라도 사용자의 트랜잭션 내에서는 일관되게 1,000원으로 처리되어 예측 가능한 경험을 제공한다.
204+
205+
결국 정답은 없다.
206+
격리 수준의 선택은 '데이터의 최신성'과 **'트랜잭션 내의 일관성(논리적 정합성)'** 사이의 트레이드오프이다.
207+
208+
- `READ COMMITTED`: 다른 트랜잭션의 변경 사항을 빠르게 반영해야 하는 금융 정보 조회 등 **데이터 최신성**이 중요할 때 유리하다. (ex: 계좌 잔액)
209+
- `REPEATABLE READ`: 하나의 비즈니스 로직이 진행되는 동안 데이터의 일관성을 유지하는 것이 더 중요할 때 유리하다. (ex: 결제 시점의 상품 가격, 재고 수량 등)
210+
211+
따라서 개발자는 RDBMS(Oracle, MySQL 등)별 기본 격리 수준을 인지하고, 서비스의 비즈니스 정책과 요구사항을 면밀히 분석하여 각 트랜잭션에 가장 적합한 격리 수준을 적용해야 한다.
212+
164213
## 예상 질문
165214

166215
* 트랜잭션 격리 수준이 필요한 이유는 무엇일까요?
@@ -179,4 +228,6 @@ author: devFancy
179228

180229
* [트랜잭션 격리 수준](https://gyoogle.dev/blog/computer-science/data-base/Transaction%20Isolation%20Level.html)
181230

182-
* [트랜잭션 - Isolation level(격리 수준)과 이상 현상](https://github.com/devfancy/2023-CS-Study/blob/main/DB/db_transaction_isolation_level.md)
231+
* [트랜잭션 - Isolation level(격리 수준)과 이상 현상](https://github.com/devfancy/2023-CS-Study/blob/main/DB/db_transaction_isolation_level.md)
232+
233+
* [[네이버페이] 실무에서 만나는 DB isolation level](https://medium.com/naverfinancial/%EC%8B%A4%EB%AC%B4%EC%97%90%EC%84%9C-%EB%A7%8C%EB%82%98%EB%8A%94-db-isolation-level-e94a904bbf9d)

0 commit comments

Comments
 (0)