본문 바로가기

Spring

JPA 비관적 락 (Pessimistic Lock)

728x90

락의 필요성

금융이나 결제쪽 개발을 할때 동시성 제어가 필요한 경우가 있다.

물론 synchronized 처리를 떠올리지만, 쓰레드 레벨로 막는 것이고 실제로는 여러대의 서버를 두기때문에 부족하다.

이 경우 DB(RDBMS, Redis 등) 레벨의 락이 필요한데, 이럴때 사용할 수 있는 JPA 락 방법이 있다.

 

락의 종류

흔히 2가지로 나뉜다.

  • 낙관적 락 (Optimistic Lock) : 이름은 락이지만, 락을 걸진않고 수정 시 업데이트 되었는가를 감지
  • 비관적 락 (Pessimistic Lock) : 다른 트랜잭션의 데이터 (조회, 수정) 혹은 수정만 막는다.

낙관적 락은 JPA에서 @Version 이라는 어노테이션을 통해 사용하고

수정 시 버전이 다른 경우, ObjectOptimisticLockingFailureException을 발생시키는 방식이다.

 

이번에 작성할 내용은 바로 비관적 락이다.

 

비관적 락 사용 방법

JPA에서 제공하는 @Lock 어노테이션을 사용한다.

interface UserRepository extends Repository<User, Long> {

  // Plain query method
  @Lock(LockModeType.READ)
  List<User> findByLastname(String lastname);
}

Spring Data JPA Document Example

 

Jpa Repository 메소드에 @Lock을 붙여서 사용하는 방식이고, LockModeType을 지정하는 것을 알 수 있다.

 

만약 JPA 기본 메소드인 findById에 붙이고 싶다면?

interface UserRepository extends Repository<User, Long> {

  // Redeclaration of a CRUD method
  @Lock(LockModeType.READ)
  Optional<User> findById(Long id);
}

단순하게 재정의 하면 되겠다.

 

개인적으로 이런식으로 개발을 하면 혼란스러울 수 있기 때문에 메소드 구분을 명확히 해주는게 좋아보인다.

findById말고 findByUserSeq 라던지

 

LockModeType

LockModeType은 이름부터 느껴지듯이 어디 수준까지 락을 걸지에 대한 모드로 보인다.

데이터 베이스에 따라 지원되지 않는 LockModeType이 있으니 주의해야한다.

public enum LockModeType {
    READ,
    WRITE,
    OPTIMISTIC,
    OPTIMISTIC_FORCE_INCREMENT,
    PESSIMISTIC_READ,
    PESSIMISTIC_WRITE,
    PESSIMISTIC_FORCE_INCREMENT,
    NONE;

    private LockModeType() {
    }
}

들어가보면 생각보다 모드가 많다.

  • NONE : 락을 걸지 않는다.
  • READ, OPTIMISTIC  : 낙관적 락을건다
  • WRITE, OPTIMISTIC_FORCE_INCREMENT  : 버전을 업데이트 시키는 낙관적 락을 건다
  • PESSIMISTIC_READ : 비관적 읽기 락
  • PESSIMISTIC_WRITE : 비관적 쓰기 락
  • PESSIMISTIC_FORCE_INCREMENT : 비관적 쓰기 락 + 버전 업데이트

 

비관적 락 사용 시 주의할 점

데드락

데드락에 빠질 수 있기 때문에, 락 타임아웃을 설정해주어야 한다.

@Transactional

락을 걸기 위해선 @Transactional 처리가 필요하다.

락을 거는 메소드를 사용하는데 @Transactional이 없는 경우 에러가 발생한다.

728x90