카샤의 만개시기

JPA에서 Optimistic Lock과 Pessimistic Lock 본문

Java/Spring

JPA에서 Optimistic Lock과 Pessimistic Lock

SKaSha 2019. 7. 8. 23:23

낙관적 락 (Optimistic Lock)

트랜잭션 대부분 충돌이 발생하지 않는다고 가정하는 방법으로써 어플리케이션이 제공하는 락 방식입니다. 그러므로 읽는 시점에 Lock을 사용하지 않기 때문에 데이터를 수정하는 시점에 다른 사용자에 의해 데이터가 변경되었는지 변경여부를 확인해야 합니다.
이렇게 낙관적 락은 트랜잭션을 커밋하기 전까지는 트랜잭션의 충돌 여부를 알 수 없습니다.

@Version

JPA가 제공하는 낙관적 락을 사용하려면 @Version 어노테이션을 사용하여 버전 관리를 추가하면 되며 지원되는 타입은 long, Long, int, Integer, short, Short, TimeStamp 이다.
@Version 어노테이션을 붙이면 엔티티가 수정될때 자동으로 버전이 하나씩 증가하며, 수정할때 조회 시점의 버전과 다를 경우 OptimisticLockException 예외가 발생한다.

Lock Modes

JPA는 두가지의 낙관적 락 모드를 지원합니다.

  • OPTIMISTIC (Read)
    @Version을 포함하는 모든 엔티티에 대한 낙관적 락을 얻는다.
    이를 통해 dirty read와 non-repeatable read를 방지한다.
  • OPTIMISTIC_FORCE_INCREMENT (Write)
    낙관적 락을 사용하면서 버전 정보를 강제로 증가시킨다.

사용법

Find

entityManager.find(Student.class, studentId, LockModeType.OPTIMISTIC);

Query

Query query = entityManager.createQuery("from Student where id = :id");
query.setParameter("id", studentId);
query.setLockMode(LockModeType.OPTIMISTIC_INCREMENT);
query.getResultList()

Explicit Locking

Student student = entityManager.find(Student.class, id);
entityManager.lock(student, LockModeType.OPTIMISTIC);

Refresh

Student student = entityManager.find(Student.class, id);
entityManager.refresh(student, LockModeType.READ);

NamedQuery

@NamedQuery(name="optimisticLock",
  query="SELECT s FROM Student s WHERE s.id LIKE :id",
  lockMode = WRITE)

비관적 락 (Pessimistic Lock)

자원에 대한 동시 요청이 발생하여 일관성에 문제가 생길 것이라고 비관적으로 생각하고 이를 방지하기 위해 우선 락을 거는 방식이다.
Pessimistic Lock은 배타적 락(Exclusive Lock)과 공유 락(Shared Lock) 두가지 타입이 있다.
Shared Lock은 다른 사용자가 동시에 데이터를 읽을 수는 있지만 Write는 할 수 없다.
Exclusive Lock은 데이터를 변경하고자 할 때 사용되며, 트랜잭션이 완료될 때까지 유지되어 해당 Lock이 해제될 때까지 다른 트랜잭션은 해당 데이터에 읽기를 포함하여 접근을 할 수 없다.
대표적으로 SELECT FOR UPDATE를 예로 들수 있다.

Lock Modes

JPA는 세가지의 비관적 락 모드를 지원합니다.

  • PESSIMISTIC_READ
    Shared Lock을 얻고 데이터가 업데이트되거나 삭제되지 않도록합니다.
  • PESSIMISTIC_WRITE
    Exclusive Lock을 획득하고 데이터를 읽거나, 업데이트하거나, 삭제하는 것을 방지합니다.
  • PESSIMISTIC_FORCE_INCREMENT
    PESSIMISTIC_WRITE와 유사하게 작동하며 엔티티의 버전 속성을 추가로 증가시킵니다.

Exception

  • PersistenceException
    한번에 하나의 Lock만 얻을 수 있으며, 락을 가져오는데 실패하면 발생하는 예외
  • LockTimeoutException
    락을 기다리다 설정해놓은 wait time을 지났을 경우 발생하는 예외
  • PersistanceException
    영속성 문제가 발생했을때의 예외

사용법

Find

entityManager.find(Student.class, studentId, LockModeType.PESSIMISTIC_READ);

Query

Query query = entityManager.createQuery("from Student where studentId = :studentId");
query.setParameter("studentId", studentId);
query.setLockMode(LockModeType.PESSIMISTIC_WRITE);
query.getResultList()

Explicit Locking

Student resultStudent = entityManager.find(Student.class, studentId);
entityManager.lock(resultStudent, LockModeType.PESSIMISTIC_WRITE);

Refresh

Student resultStudent = entityManager.find(Student.class, studentId);
entityManager.refresh(resultStudent, LockModeType.PESSIMISTIC_FORCE_INCREMENT);

NamedQuery

@NamedQuery(name="lockStudent",
  query="SELECT s FROM Student s WHERE s.id LIKE :studentId",
  lockMode = PESSIMISTIC_READ)

Lock Scope

  • PessimisticLockScope.NORMAL
    기본값으로써 해당 entity만 lock이 걸립니다.
    @Inheritance(strategy = InheritanceType.JOINED)와 같이 조인 상속을 사용하면 부모도 함께 잠급니다.
  • PessimisticLockScope.EXTENDED
    @ElementCollection, @OneToOne , @OneToMany 등 연관된 entity들도 lock이 됩니다.

'Java > Spring' 카테고리의 다른 글

RestTemplate  (2) 2019.07.07
Spring AOP, Proxy  (0) 2019.07.04
Comments