매일 매일, 차곡 차곡 쌓기



완벽하지 않은 것을 두려워 말며,
완성도를 높히는데 집중하자.

Spring/JPA

JPA 낙관적 락

blockbuddy93 2023. 12. 7. 23:53

낙관적 락(Optimistic Locking)이란

  • 동시성 제어 기법 중 낙관적 락은 트랜잭션 충돌을 기대하지 않고, 일단 트랜잭션을 수행하고 나중에 충돌을 해결하는 방식.  
  • 트랜잭션이 데이터를 읽을 때는 어떠한 락도 획득하지 않고, 수정이 필요한 경우에만 해당 데이터의 버전을 확인하여 충돌 여부를 판단
  • 데이터를 수정하기 전에 해당 데이터의 버전을 확인하고, 만약 다른 트랜잭션에 의해 이미 변경되었다면 충돌이 발생한 것으로 간주하여 롤백하거나 충돌을 해결하는 방법을 적용
  • 일반적으로 낙관적 락은 읽기 작업이 많고 충돌이 드물거나 극히 적은 경우에 사용.
  • 낙관적 락은 데이터베이스가 제공하는 락 기능을 사용하는 것이 아니라, JPA가 제공하는 버전 관리 기능을 사용

 

JPA 낙관적 락 활용

JPA(Java Persistence API)에서 낙관적 락을 활용하기 위해서는 엔티티의 상태를 추적하기 위한 버전 관리 필드를 도입해야 함.

일반적으로 @Version 애노테이션을 통해 이를 구현하며, 아래는 JPA에서 낙관적 락을 적용하는 기본적인 방법.

  1. 버전 관리 필드 추가: 엔티티 클래스에 버전 관리 필드를 추가. 이 필드는 엔티티의 상태를 추적하는 데 사용됨.
import javax.persistence.*;

@Entity
public class YourEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    // 다른 필드들...

    @Version
    private Long version; // 버전 관리 필드
}
  1. 엔티티 수정 시 버전 증가: 엔티티를 수정할 때마다 버전 필드가 증가하도록 해야 함. JPA는 엔티티를 수정할 때마다 자동으로 버전 필드를 업데이트.
  2. 트랜잭션 충돌 처리: 낙관적 락을 사용하는 경우 트랜잭션 충돌이 발생할 수 있음. 엔티티를 읽은 후에 엔티티를 수정하기 전에 해당 엔티티의 버전이 변경되었는지 확인해야 함. 변경 여부를 확인하고 충돌을 처리하는 방법은 다음과 같음:
    • 엔티티를 읽은 후에 엔티티를 수정하기 전에 해당 엔티티의 버전을 다시 확인.
    • 버전이 변경되지 않았다면 엔티티를 수정하고 커밋.
    • 버전이 변경되었다면 충돌이 발생한 것으로 간주하여 다시 읽거나 충돌을 해결하는 방법을 선택.

JPA를 사용하여 낙관적 락을 구현할 때 주의할 점은 트랜잭션 커밋 시에만 버전이 업데이트된다는 것. 따라서 엔티티를 수정할 때마다 버전이 자동으로 증가하는 것을 보장할 수 있음.

 

 

낙관적 락에서 발생하는 예외

낙관적 락은 트랜잭션 커밋하는 시점에 충돌을 알 수 있다는 특징이 있으며, 발생하는 예외는 다음과 같음

  1. OptimisticLockException
  2. StableObjectstateException
  3. ObjectOptisticLockingFailureExcetpion

낙관적 락 충돌 해결 전략

1. None

락옵션을 사용하지 않아도 엔티티에 @Version 이 적용된 필드만 있으면 낙관적 락이 적용된다.

  • 용도 : 조회한 엔티티를 수정할 때 다른 트랜잭션에 의해 변경(삭제) 되지 않아야 한다. 조회 시점부터 수정 시점까지를 보정한다.
  • 동작 : 엔티티를 수정할 때 버전을 체크하면서 버전을 증가한다(update 쿼리 사용) 이때 데이터베이스 버전 값이 현재 버전이 아니면 예외가 발생한다.
  • 이점 : 두번의 갱실 분실 문제를 예방한다.

 

2. Optimistic

@Version만 적용했을 때는 엔티티를 수정해야 버전을 체크하지만 옵션을 추가하면 엔티티를 조회만 해도 버전을 체크한다.

쉽게 이야기해서 한 번 조회한 엔티티는 트랜잭션을 종료할 때까지 다른 트랜잭션에서 변경하지 않음을 보장한다.

  • 용도 : 조회한 엔티티는 트랜잭션이 끝날 때까지 다른 트랜잭션에 의해 변경되지 않아야 한다.
  • 조회 시점부터 트랜잭션이 끝날 때까지 조회한 엔티티가 변경되지 않음을 보장한다.
  • 동작 : 트랜잭션을 커밋할 때 버전 정보를 조회해서(select 쿼리 사용) 현재 엔티티의 버전과 같은지 검증한다. 만약 같지 않으면 예외가 발생한다.
  • 이점 : optimistic 옵션은 dirty read와 non-reatable read를 방지한다.
// 트랜잭션 1 조회 title 제목 = '제목A' version 1
Board board = em.find(Board.class, id, LockModeType.Optimistic);

// 중간에 트랜잭션 2에서 해당하는 게시물을 수정해서 title=제목c, version 2로 증가

// 트랜잭션 1 커밋시점에 버전 정보 검증, 예외 발생
tx.commit()

 

 

3. Optimistc_force_increment

낙관적 락을 사용하면서 버전 정보를 강제로 증가한다.

  • 용도 : 논리적인 단위의 엔티티 묶음을 관리할 수 있다. 예를 들어 게시물과 첨부파일이 일대다. 다대일의 양방향 연관관계이고 첨부파일이 연관관계의 주인이다. 게시물을 수정하는 데 단순히 첨부파일만 추가하면 게시물의 버전은 증가하지 않는다. 해당 게시물은 물리적으로 변경되지 않았지만, 논리적으로 변경되었다. 이때 게시물의 버전도 강제로 증가하려면 Optimistic_force_increament를 사용할 수 있다.
  • 동작 : 엔티티를 수정하지 않아도 트랜잭션을 커밋할때 update 쿼리를 사용해서 버전 정보를 강제로 증가시킨다. 이때 데이터베이스의 버전이 엔티티 버전과 다르면 예외가 발생한다. 추가로 엔티티를 수정하면 수정시 버전 udpate가 발생한다. 따라서 총 2번의 버전 증가가 나타날 수 있다.
  • 이점 : 강제로 버전을 증가해서 논리적인 단위의 엔티티 묶음을 버전 관리 할 수 있다.

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

성능 향상을 위한 1차 캐시와 2차 캐시  (0) 2023.12.31
JPA 비관적 락  (1) 2023.12.08
영속성 컨텍스트란?  (0) 2023.12.03
JPQL 이란?  (0) 2023.12.02
JPA 란?  (1) 2023.12.02