JPA 낙관적 잠금(Optimistic Lock), 비관적 잠금(Pessimistic Lock)

pastaCoder·2022년 11월 1일
0

비관적 락

  • 트랜잭션 충돌이 발생한다고 가정하고 락을 거는 기법.
  • DB가 제공하는 락 기능을 사용함. (MySQL기준 SELECT FOR UPDATE)
  • 데이터를 수정 시 바로 트랜잭션 충돌을 알 수 있음.

낙관적 락

  • 트랜잭션 충돌이 거의 없다고 가정될때 사용되는 기법.
  • DB가 제공하는 락 기능을 사용하지 않고 JPA가 제공하는 버전 관리 기능을 사용함(필자는 @Version 어노테이션을 이용해 구현함)
  • 트랜잭션을 커밋후에 트랜잭션의 충돌을 알 수 있음.

예시

우선 문제상황은 분산 환경에서 여러대의 서버가 하나의 데이터베이스에 접근한다고 할 때, 서버 A가 DB에 접근해서 SELECT를 한 뒤, 거의 동시에 서버 B가 같은 DB table에 접근해서 SELECT를 했다면 아래와같은 상황이된다.

서버 A서버 B
id11
상품 수량100100
상품 식별번호12341234

아래의 표처럼 둘이 동일한 정보를 읽게되버린다. 위의 상태에서 각각의 서버에 수량을 5씩 감소시키는 명령이 들어온다면, 예상한 결과는 각각 5씩 감소시켜 90이 되야하지만 실제로는 95가된다.
이런 형태이다.

서버 A서버 B
id11
상품 수량9595
상품 식별번호12341234

각각 100에서 5씩 감소시킨 95를 db에 기록하여 문제가 생긴다. 해당 사항을 어떻게 해결해야할까? 그때 사용하는 방법중 하나가 lock이다. 다른 종류도 있지만, 추후에 설명하고 이 글에서는 비관적 락, 낙관적 락만 설명하겠다.

비관적 락을 걸게될 경우, db의 row자체에 lock을 걸어 row를 가져간 트랜잭션의 commit이 완료될때까지 설정에 따라 읽기나 쓰기같은 행위가 동작하지않는다. spring으로 동작시켰을때는 쿼리가

select * from product ~~~ for update;

이런식으로 날아갔었다.

낙관적 락을 이용할 경우에는 Product Entity에 version 컬럼을 하나 만들어, 실제로 커밋을 했을때 버전 정보에 문제가 없는지 검사하여 로직을 짜봤고, 둘 다 동시성을 보장하며 제대로 작동하는것을 확인했다.
아래는 해당 테스트의 깃허브 링크다.
https://github.com/xanqus/spring-concurrency-problem_exam

0개의 댓글