Topic (오늘의 주제)
데이터베이스 동시성 이슈 (Concurrency Issues)
(Dirty Read, Non-Repeatable Read, Phantom Read)
Why (왜 사용하는가? 왜 중요한가?)
- 실무: 동시에 여러 사용자가 티켓을 예매하거나 결제를 시도할 때, 데이터의 **무결성(Integrity)**이 깨지는 것을 막기 위해 반드시 이해해야 합니다. (예: 남은 티켓은 1장인데 2명이 동시에 결제 성공하는 'Double Spending' 문제)
- 구조적 의미: 데이터베이스는 성능(동시 처리량)과 데이터 정확성(격리성) 사이에서 균형을 잡아야 합니다. 이 이슈들을 해결하기 위해 **트랜잭션 격리 수준(Isolation Level)**이 존재합니다.
- 면접 의도: ACID(특히 Isolation) 속성에 대한 깊은 이해도와, 실무에서 발생하는 **Race Condition(경쟁 상태)**을 어떻게 해결하는지(Locking, MVCC 등)를 평가합니다.
Core Concept (핵심 개념 정리)
동시성 이슈는 주로 여러 트랜잭션이 동시에 실행될 때, 서로의 작업에 간섭하여 발생하는 데이터 불일치 현상입니다.
| 이슈 (Anomaly) | 개념 정의 | 발생 원인 | 해결책 (표준 격리 수준) |
| Dirty Read (더티 리드) |
다른 트랜잭션이 수정 중이고 아직 커밋하지 않은 데이터를 읽는 현상. | 트랜잭션 A가 수정 후 롤백했는데, B가 이미 수정된(유효하지 않은) 값을 읽고 로직을 수행함. | Read Committed 이상 설정. |
| Non-Repeatable Read (반복 불가능한 조회) |
한 트랜잭션 내에서 같은 데이터를 두 번 조회했는데 그 값이 다른 현상. | 트랜잭션 A가 조회 → 트랜잭션 B가 수정(UPDATE) 및 커밋 → A가 다시 조회 (값이 바뀜). | Repeatable Read 이상 설정. |
| Phantom Read (유령 읽기) |
한 트랜잭션 내에서 범위 조회를 두 번 했는데, 없던 데이터가 생기거나 사라지는 현상. | 트랜잭션 A가 조회 → 트랜잭션 B가 삽입(INSERT) 또는 삭제(DELETE) → A가 다시 조회 (행 개수가 다름). | Serializable 또는 Next-Key Lock. |
+ 추가: Lost Update (갱신 손실)
두 트랜잭션이 동시에 같은 데이터를 수정하려 할 때, 나중에 완료된 트랜잭션이 이전 트랜잭션의 수정 내용을 덮어씌워버리는 현상입니다. (실무에서 가장 치명적인 문제)
Interview Answer Version (면접 답변식 요약)
"데이터베이스 동시성 이슈는 다수의 트랜잭션이 동시에 처리될 때 격리성이 보장되지 않아 발생하는 문제입니다.
대표적으로 커밋되지 않은 데이터를 읽는 Dirty Read, 조회 시마다 값이 바뀌는 Non-Repeatable Read, 없던 데이터가 튀어나오는 Phantom Read가 있습니다.
이러한 문제는 트랜잭션 격리 수준을 높여서 해결할 수 있지만, 격리 수준이 높을수록 성능이 떨어지므로 서비스의 특징에 맞춰 MVCC나 낙관적/비관적 락을 적절히 활용해야 합니다."
Practical Tip (사용시 주의할 점 or 활용 예)
1. 갱신 손실(Lost Update) 방지 전략
재고 차감이나 좋아요 수 증가 같은 로직에서 가장 빈번하게 발생합니다.
- Atomic Update 사용: 값을 애플리케이션으로 가져와서 계산하지 말고 DB 레벨에서 연산합니다.
- UPDATE product SET stock = stock - 1 WHERE id = 1; (DB가 해당 Row에 Lock을 걸어 순차 처리됨)
- 비관적 락 (Pessimistic Lock): 데이터를 읽을 때부터 락을 겁니다. (SELECT ... FOR UPDATE)
- 낙관적 락 (Optimistic Lock): 락을 걸지 않고 version 컬럼을 이용해 커밋 시점에 충돌을 감지하고 재시도합니다. (JPA @Version)
2. 사용하는 DB의 기본 격리 수준 확인
- MySQL(InnoDB): 기본이 Repeatable Read입니다. 따라서 Dirty Read, Non-Repeatable Read는 발생하지 않지만, Phantom Read는 발생할 수 있습니다. (단, InnoDB는 Next-Key Lock으로 Phantom Read도 어느 정도 방지함)
- Oracle, PostgreSQL: 기본이 Read Committed입니다. 하나의 트랜잭션 안에서도 조회 값이 달라질 수 있음을 인지하고 코드를 짜야 합니다.
예상 꼬리질문 정리
Q1. 트랜잭션 격리 수준(Isolation Level) 4단계에 대해 설명해주세요.
- 핵심 키워드:
- Read Uncommitted: 커밋 안 된 데이터도 읽음 (Dirty Read 발생).
- Read Committed: 커밋된 데이터만 읽음 (가장 많이 사용).
- Repeatable Read: 트랜잭션 내에서 항상 같은 값을 읽음 (MySQL 기본).
- Serializable: 가장 엄격함. 읽기 작업에도 락을 걸어 동시성 가장 낮음.
Q2. MVCC(Multi-Version Concurrency Control)란 무엇인가요?
- 핵심 키워드: 락(Lock)을 걸지 않고 읽기 일관성을 보장하는 기술입니다. 데이터를 덮어쓰는 게 아니라 **변경 전의 데이터를 Undo 영역(스냅샷)**에 저장해두고, 다른 트랜잭션이 조회하면 이 스냅샷을 보여줌으로써 읽기와 쓰기가 서로를 방해하지 않게 합니다.
Q3. 낙관적 락(Optimistic Lock)과 비관적 락(Pessimistic Lock)은 각각 언제 사용하는 게 좋나요?
- 핵심 키워드: 낙관적 락은 충돌이 거의 없을 것이라 가정하는 경우(조회 위주)에 사용하여 성능이 좋지만 충돌 시 롤백 비용이 큽니다. 비관적 락은 데이터 충돌이 잦은 경우(재고 차감, 선착순)에 사용하여 데이터 무결성을 확실히 보장하지만 동시성 성능이 떨어질 수 있습니다.
'Archive > Daily Dev Q&A' 카테고리의 다른 글
| Daily Dev Q&A: 트랜잭션 격리 수준 (0) | 2026.02.02 |
|---|---|
| Daily Dev Q&A: 데이터베이스 락 (0) | 2026.02.02 |
| Daily Dev Q&A: 트랜잭션과 인덱스의 관계 (0) | 2026.01.29 |
| Daily Dev Q&A: 관계형 데이터베이스의 인덱스 (1) | 2026.01.27 |
| Daily Dev Q&A: Cookie vs Session (0) | 2026.01.23 |