Skip to content

동시성 문제와 격리 수준 (Lost Update · Dirty Read · Phantom Read) ​

여러 사람이 같은 데이터를 동시에 읽고 고칠 때 생기는 5가지 문제와, 데이터베이스가 이를 막기 위해 쓰는 트랜잭션 격리 수준을 정리했습니다.

왜 동시성 문제가 생기나 ​

**트랜잭션(Transaction)**은 "한 번에 처리돼야 하는 작업 묶음"입니다. 계좌 이체라면 "A 계좌에서 빼기 + B 계좌에 넣기"가 한 트랜잭션입니다. 작업을 확정하는 것을 커밋(Commit), 없던 일로 되돌리는 것을 **롤백(Rollback)**이라고 합니다.

혼자 쓰는 DB라면 문제가 없지만, 실제 서비스에서는 수많은 트랜잭션이 동시에 같은 데이터를 건드립니다. 이때 서로를 적절히 떼어놓지(격리, Isolation) 않으면 데이터가 엉키는 현상이 생깁니다. 공유 문서를 두 사람이 동시에 고치다가 한 사람 수정분이 날아가는 상황을 떠올리면 됩니다.

동시성 문제 5가지 ​

1. 갱신 손실 (Lost Update) ​

먼저 시작한 트랜잭션의 수정 내용을 뒤따라온 트랜잭션이 덮어써서, 한쪽 수정이 사라지는 현상입니다.

잔액 10,000원
A: 10,000 → 15,000 으로 수정 (아직 커밋 전)
B: 10,000 → 20,000 으로 수정 후 커밋
A: 커밋
→ 둘 중 한쪽의 수정이 덮어써져서 흔적도 없이 사라짐

2. 더티 리드 (Dirty Read) ​

아직 커밋되지 않은 중간 데이터를 다른 트랜잭션이 읽어 가는 현상입니다. 먼저 트랜잭션이 롤백되면, 읽어 간 쪽은 존재한 적 없는 값으로 일한 셈이 됩니다. "현상 유지 오류"라고도 부릅니다.

A: 상품 가격 5,000 → 3,000 으로 수정 (커밋 전)
B: 3,000원을 읽어서 계산에 사용
A: 문제가 생겨 롤백 → 다시 5,000원
→ B는 세상에 없던 3,000원으로 계산해 버림

3. 비반복 읽기 (Non-Repeatable Read) ​

한 트랜잭션 안에서 같은 데이터를 두 번 읽었는데 결과가 다른 현상입니다. 그 사이에 다른 트랜잭션이 값을 **수정(Update)하거나 삭제(Delete)**했기 때문입니다.

A: 홍길동 등급 조회 → 'GOLD'
B: 홍길동 등급을 'VIP'로 수정 후 커밋
A: 다시 조회 → 'VIP'
→ 한 작업 안에서 같은 질문에 다른 답이 나옴

4. 유령 읽기 (Phantom Read) ​

한 트랜잭션 안에서 같은 범위를 두 번 조회했는데 행 개수가 달라지는 현상입니다. 그 사이 다른 트랜잭션이 새 행을 **삽입(Insert)**해서, 없던 데이터가 유령처럼 나타납니다.

A: 등급이 'VIP'인 회원 수 조회 → 5명
B: 새 VIP 회원 1명 추가 후 커밋
A: 다시 조회 → 6명

비반복 읽기 vs 유령 읽기

둘 다 "두 번 읽었더니 다르다"는 점은 같습니다. 비반복 읽기는 이미 있던 행의 값이 바뀐 것(수정/삭제), 유령 읽기는 결과 집합에 행이 새로 생긴 것(삽입)이라는 차이로 구분하면 됩니다.

5. 모순성 (Inconsistency) ​

여러 데이터를 차례로 읽는 도중에 다른 트랜잭션이 그 데이터를 바꿔서, 서로 맞지 않는 시점의 값들을 섞어 계산하게 되는 현상입니다.

A: 1번 계좌와 2번 계좌의 합계를 구하려고 1번 계좌를 읽음
B: 2번 → 1번 계좌로 이체 완료 후 커밋
A: 2번 계좌를 읽음
→ 이체 전 1번 계좌 + 이체 후 2번 계좌를 더해서 틀린 합계가 나옴

해결책: 트랜잭션 격리 수준 ​

데이터베이스는 트랜잭션끼리 얼마나 떼어놓을지를 **4단계 격리 수준(Isolation Level)**으로 정해 둡니다. 아래로 갈수록 더 엄격해서 막아주는 문제가 많아지지만, 서로 기다리는 시간이 늘어 동시에 처리할 수 있는 양(성능)은 줄어듭니다.

격리 수준뜻Dirty ReadNon-Repeatable ReadPhantom Read
Read Uncommitted커밋 안 된 값도 읽음발생발생발생
Read Committed커밋된 값만 읽음막음발생발생
Repeatable Read트랜잭션 동안 읽은 값은 그대로 유지막음막음발생
Serializable트랜잭션을 한 줄로 세운 것처럼 처리막음막음막음

많은 DB가 기본값으로 Read Committed(Oracle, PostgreSQL 등)나 Repeatable Read(MySQL InnoDB)를 씁니다. 가장 안전한 Serializable은 성능 부담이 커서 꼭 필요한 곳에만 씁니다.

정리 ​

문제한 줄 요약원인이 되는 작업
갱신 손실한쪽 수정이 덮어써져 사라짐동시 수정
더티 리드커밋 전 값을 읽었는데 롤백됨커밋 전 읽기
비반복 읽기같은 행을 두 번 읽었더니 값이 다름중간에 Update/Delete
유령 읽기같은 범위를 두 번 읽었더니 행이 늘어남중간에 Insert
모순성서로 다른 시점의 값을 섞어 계산읽는 도중 변경
  • 해결책은 격리 수준: Read Uncommitted → Read Committed → Repeatable Read → Serializable 순으로 엄격해집니다.
  • 엄격할수록 안전하지만 느려지는 트레이드오프가 있습니다.

참고: 개인 Notion 학습 노트