낙관적 락(Optimistic Locking)이란
- 동시성 제어 기법 중 낙관적 락은 트랜잭션 충돌을 기대하지 않고, 일단 트랜잭션을 수행하고 나중에 충돌을 해결하는 방식.
- 트랜잭션이 데이터를 읽을 때는 어떠한 락도 획득하지 않고, 수정이 필요한 경우에만 해당 데이터의 버전을 확인하여 충돌 여부를 판단
- 데이터를 수정하기 전에 해당 데이터의 버전을 확인하고, 만약 다른 트랜잭션에 의해 이미 변경되었다면 충돌이 발생한 것으로 간주하여 롤백하거나 충돌을 해결하는 방법을 적용
- 일반적으로 낙관적 락은 읽기 작업이 많고 충돌이 드물거나 극히 적은 경우에 사용.
- 낙관적 락은 데이터베이스가 제공하는 락 기능을 사용하는 것이 아니라, JPA가 제공하는 버전 관리 기능을 사용
JPA 낙관적 락 활용
JPA(Java Persistence API)에서 낙관적 락을 활용하기 위해서는 엔티티의 상태를 추적하기 위한 버전 관리 필드를 도입해야 함.
일반적으로 @Version 애노테이션을 통해 이를 구현하며, 아래는 JPA에서 낙관적 락을 적용하는 기본적인 방법.
- 버전 관리 필드 추가: 엔티티 클래스에 버전 관리 필드를 추가. 이 필드는 엔티티의 상태를 추적하는 데 사용됨.
import javax.persistence.*;
@Entity
public class YourEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// 다른 필드들...
@Version
private Long version; // 버전 관리 필드
}
- 엔티티 수정 시 버전 증가: 엔티티를 수정할 때마다 버전 필드가 증가하도록 해야 함. JPA는 엔티티를 수정할 때마다 자동으로 버전 필드를 업데이트.
- 트랜잭션 충돌 처리: 낙관적 락을 사용하는 경우 트랜잭션 충돌이 발생할 수 있음. 엔티티를 읽은 후에 엔티티를 수정하기 전에 해당 엔티티의 버전이 변경되었는지 확인해야 함. 변경 여부를 확인하고 충돌을 처리하는 방법은 다음과 같음:
- 엔티티를 읽은 후에 엔티티를 수정하기 전에 해당 엔티티의 버전을 다시 확인.
- 버전이 변경되지 않았다면 엔티티를 수정하고 커밋.
- 버전이 변경되었다면 충돌이 발생한 것으로 간주하여 다시 읽거나 충돌을 해결하는 방법을 선택.
JPA를 사용하여 낙관적 락을 구현할 때 주의할 점은 트랜잭션 커밋 시에만 버전이 업데이트된다는 것. 따라서 엔티티를 수정할 때마다 버전이 자동으로 증가하는 것을 보장할 수 있음.
낙관적 락에서 발생하는 예외
낙관적 락은 트랜잭션 커밋하는 시점에 충돌을 알 수 있다는 특징이 있으며, 발생하는 예외는 다음과 같음
- OptimisticLockException
- StableObjectstateException
- 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 |