주제
- 비관적 락과 낙관적 락에 대해 알아본다.
- 각각 적합한 사용처를 알아본다.
1. 비관적 락, 낙관적 락
1.1 락이 왜 필요한가요?
트래픽이 낮은 서비스에서는 자주 발생하지 않겠지만, 트래픽이 많아진다면 여러 트랜잭션/요청이 동시에 같은 데이터에 접근하는 경우가 생길 수 있다.
이때 각기 다른 요청이 수행하는 작업은 서로의 작업 결과에 영향 끼칠 수 있다.
데이터의 일관성을 유지하기 위해 Lock을 통해 이를 제어할 수 있다.
1.1 비관적 락(Pessimistic Lock)
1.1.1 정의
자원 요청에 따른 동시성 문제가 발생할 것이라 예상하고 락을 거는 방법론
- 트랜잭션 간에 충돌이 발생할 것이라고 가정한다.
- 하나의 트랜잭션이 자원에 접근했을 때 락을 걸고, 다른 트랜잭션은 접근하지 못한다.
- DB에서는 Shared Lock(공유 잠금: Select) 이나 Exclusive Lock(배타 잠금: Insert, Update, Delete)을 건다
- S Lock : 하나의 트랜잭션이 자원을 읽는 동안 다른 트랜잭션이 S Lock은 가능하나 E Lock은 걸 수 없다.
- E Lock : 하나의 트랜잭션이 자원을 변경할 때, 다른 트랜잭션이 S Lock, E Lock을 걸 수 없다
1.1.2 장/단점
- 장점
- 충돌이 자주 발생하는 환경에서 롤백의 횟수를 줄일 수 있어 성능에 유리
- 데이터 무결성을 보장하는 수준이 높다
- 단점
- 동시성이 떨어져 성능 손해를 많이 본다.
- 읽기가 많이 이루어지는 곳에는 적합하지 않다.
- 데드락을 유발할 수 있다.
1.2 낙관적 락(Optimistic Lock)
1.2.1 정의
자원을 접근할 때 락을 걸지 않고, 동시성 문제가 발생할 때 처리한다는 방법론
- 트랜잭션 간에 충돌이 발생하지 않을 것이라 가정한다.
- 충돌이 나는 것을 막지 않고 충돌이 났을 때 처리한다.
- Version의 상태를 통해 충돌을 확인한다.
- 어플리케이션 단에서 처리한다.
1.2.2 장/단점
- 장점
- 충돌이 안난다는 가정하에 요청이 수행되므로 처리 성능이 좋다.
- 단점
- 잦은 충돌이 일어나서 처리 비용이 많이 들 때는 적합하지 않다.
- 어플리케이션단에서 롤백 처리를 어떻게 할지 구현해야 한다.
2. JPA에서의 사용법
2.1 @Version 어노테이션을 이용한 낙관적 락(Optimistic Lock)
2.1.1 @Version 어노테이션 사용법
@Entity
public class MyDataEntity {
...
@Version
private Long version;
}
- 엔티티 내부에 @Version 어노테이션이 붙은 변수를 통해 간단하게 구현할 수 있다.
- 엔티티 클래스에 하나의 버전 속성만 있어야 한다.
- 엔티티를 수정할 때 마다 Versioning이 되기 때문에 조회 버전과 수정 버전이 다르면 ObjectOptimisticLockingFailureException 예외가 발생한다.
- 버전에 명시할 타입은 int, Integer, long, Long, shot, Short, java.sql.Timestamp 이어야 한다.
- LockModType의 종류
- OPTIMISTIC
- 트랜잭션 시작 시 버전 점검이 수행되고, 트랜잭션 종료 시에도 버전 점검이 수행된다.
- 버전이 다르면 트랜잭션이 롤백된다.
- OPTIMISTIC_FORCE_INCREMENT
- 낙관적 락을 사용하면서 추가로 버전을 강제로 증가시킨다.
- 관계를 가진 다른 엔터티가 수정되면 버전이 변경된다. (ex. 댓글이 수정되면 게시글도 버전이 변경된다.)
- READ
- OPTIMISTIC과 동일하다.
- WRITE
- OPTIMSTIC_FORCE_INCREMENT와 동일하다.
- NONE
- 엔터티에 @Version이 적용된 필드가 있으면 낙관적 락을 적용한다.
- OPTIMISTIC
2.2 @Lock 어노테이션을 이용한 비관적 락(PessimisticLock)
2.2.1 @Lock 어노테이션 사용법
public interface UserRepository extends JpaRepository<UserEntity, Long> {
@Lock(LockModeType.PESSIMISTIC_WRITE)
Optional<UserEntity> findByIdForUpdate(Long id);
}
- LockModType의 종류
- PESSIMISTIC_READ : 다른 트랜잭션에서 읽기만 가능
- PESSIMISTIC_WRITE : 다른 트랜잭션에서 읽기도 못하고 쓰기도 못함
- PESSIMISTIC_FORCE_INCREMENT : 다른 트랜잭션에서 읽기도 못하고 쓰기도 못함 + 추가적으로 버저닝을 수행한다.
# 참고자료
- https://jaehoney.tistory.com/159
- https://isntyet.github.io/jpa/JPA-%EB%B9%84%EA%B4%80%EC%A0%81-%EC%9E%A0%EA%B8%88(Pessimistic-Lock)/
728x90
'# Back-End > Spring' 카테고리의 다른 글
ShedLock을 이용한 스케줄 작업 중복 방지하기 (0) | 2023.08.29 |
---|---|
스프링 배치 익혀보기 : 개념 정리 + 간단한 실습 (0) | 2023.08.03 |
Insert문 전에 발생하는 Select문을 제거해보자 (0) | 2023.04.15 |
JPA Batch Insert/Update를 적용하여 성능 향상하기 (0) | 2023.04.15 |
@Transactional 어노테이션을 이용한 트랜잭션 관리 (0) | 2023.04.08 |
[Spring Data JPA] Pageable을 이용한 Paging 조회 (0) | 2022.10.01 |
Logback과 slf4j 간단한 예제 (0) | 2022.09.02 |