상황 : 좋아요와 댓글 수에 대해 반정규화를 진행하였다.
문제 : 좋아요나 댓글 수에 대해 동시에 값이 증가하거나 감소하는 트랜잭션이 동시에 발생하면 값이 씹히는 상황이 발생해 올바른 갯수가 반환되지 않는다.
해결 방법 : DB단의 Lock을 사용하자
비관적 락
- 트랜잭션이 시작될 때 데이터에 락을 걸어 다른 트랜잭션이 접근하지 못하게 한다.
- 이때 user와 study가 존재할 때, study 데이터에 락을 걸면 user 데이터에 대해서는 접근이 가능하지만 study 데이터에 대해서는 트랜잭션이 끝날 때까지 대기한다. 이때 단순한 조회의 경우는 접근이 가능하다.
- 동시성 문제를 해결할 수 있지만, 성능 저하를 초래할 수 있다.
- 데드락이 발생할 수 있으므로 로직을 잘 짜야 한다.
- 동시성 충돌이 빈번히 발생하고, 데이터 무결성이 중요한 경우에 사용한다.
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select s from Study s where s.id = ?1")
Optional<Study> findByIdUsingPessimisticLock(Long studyId);
낙관적 락
- 트랜잭션이 데이터를 수정할 때 수정했음을 명시하여 다른 트랜잭션이 값을 수정할 수 없게 하는 것이다.
- 별도의 version 컬럼을 추가한다.
- version의 상태를 보고 충돌을 확인한다.
- 비관적 락은 충돌을 db단에서 트랜잭션 대기를 함으로써 해결하지만 낙관적 락은 애플리케이션 단에서 처리한다.
- 기본적으로 0개의 row를 업데이트 했다고 반환한다.
- jpa에서는 ObjectOptimisticLockingFailureException 예외를 발생시켜 준다.
- 개발자가 재시도 로직을 작성해야 한다.
- 동시성 충돌이 드물고, 성능이 중요한 경우에 사용한다.
public class ExampleEntity {
...
@Version
private Integer version;
}
'Spring Boot' 카테고리의 다른 글
@SuperBuilder (0) | 2025.02.25 |
---|---|
Spring Boot 스웨거(Swagger UI) 적용 (0) | 2025.02.16 |
Container & BeanFactory & ApplicationContext (0) | 2025.02.01 |
커서 기반 페이지네이션 추가 정보 (0) | 2025.01.31 |
지연 로딩 조회 - getReferenceById (0) | 2024.12.10 |