[CS] 동시성 이슈와 데이터 정합성: 비관적 락 vs 낙관적 락
[CS] 동시성 이슈와 데이터 정합성: 비관적 락 vs 낙관적 락
동시성은 여러 작업이 동시에 실행되더라도 전체 결과가 올바르게 유지되도록 설계하는 문제와 맞닿아 있다. 이 주제를 설명할 때 자주 등장하는 예시가 식사하는 철학자 문제다.
다섯 명의 철학자가 하나의 원탁에 앉아 식사를 한다. 각각의 철학자들 사이에는 포크가 하나씩 있고, 앞에는 접시가 있다. 접시에 담긴 음식은 포크 두 개를 사용해야만 먹을 수 있는 스파게티다.
철학자들은 서로 대화하지 않으며, 번갈아가며 식사하거나 생각만 할 수 있다. 따라서 식사를 하려면 왼쪽과 오른쪽의 포크를 모두 확보해야 한다.
또한 식사를 마친 뒤에는 포크를 다시 내려놓아 다른 철학자도 사용할 수 있어야 한다. 이때 어떤 철학자도 굶지 않고 식사할 수 있게 만드는 방법은 무엇일까?
문제 상황
둘 이상의 사용자가 거의 동시에 요청을 보내거나 서버 처리 지연이 생기면, 같은 데이터를 서로 다른 시점에 수정하면서 정합성이 깨질 수 있다.
문제 해결 방안
비관적 락 (Pessimistic Lock)
- 데이터에 먼저 락을 걸어 다른 트랜잭션의 접근을 제한하는 방식
Exclusive Lock이 걸리면 락이 해제되기 전까지 다른 트랜잭션이 해당 데이터를 수정하기 어렵다- “충돌이 발생할 가능성이 높다”는 전제에서 접근을 막고 시작한다
- 데드락이 발생할 수 있다
- 충돌이 자주 발생하는 상황에 적합하다
JPA에서는 다음과 같이 사용할 수 있다.
1
2
3
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select s from Stock s where s.id = :id")
Stock findByIdWithPessimisticLock(Long id);
낙관적 락 (Optimistic Lock)
- 실제 락을 걸지 않고
버전값을 기준으로 충돌을 감지하는 방식 - 데이터를 읽은 뒤
UPDATE를 수행할 때, 내가 읽은 버전이 아직 유효한지 확인한다 - 평소에는 자원을 선점하지 않고, 충돌이 발생했을 때만 처리한다
- 버전 충돌이 발생하면 애플리케이션 레벨에서 재시도나 롤백 처리가 필요하다
- 충돌 빈도가 낮은 상황에 적합하다
JPA에서는 다음과 같이 사용할 수 있다.
1
2
3
@Lock(LockModeType.OPTIMISTIC)
@Query("select s from Stock s where s.id = :id")
Stock findByIdWithOptimisticLock(Long id);
DB 락 외에도 Redis나 ZooKeeper 같은 도구를 이용한 분산 락으로 문제를 해결할 수 있다. 이 부분은 추후 따로 공부해서 별도 글로 정리해볼 생각이다.
This post is licensed under CC BY 4.0 by the author.