상황 : 좋아요와 댓글 수에 대해 반정규화를 진행하였다.

 

문제 : 좋아요나 댓글 수에 대해 동시에 값이 증가하거나 감소하는 트랜잭션이 동시에 발생하면 값이 씹히는 상황이 발생해 올바른 갯수가 반환되지 않는다.

 

해결 방법 : 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 예외를 발생시켜 준다.
  • 개발자가 재시도 로직을 작성해야 한다.
  • 동시성 충돌이 드물고, 성능이 중요한 경우에 사용한다.

조회할 때의 버전과 수정하기 직전에 버전이 같아야 수정을 진행한다. 이후 version의 값을 증가시킨다.

public class ExampleEntity {

    ...

    @Version
    private Integer version;
	
}

+ Recent posts