JPA를 사용하여 데이터베이스에 락을 거는 방법은 2가지방법을 뽑아 볼 수 있습니다.
테이블에 행하는 행위 ( 조회, 수정, 등록, 삭제 )를 막는 비관적 락과 데이터에 버전을 명시하고 해당 버전을 통해 데이터의 일관성을 보장하는 낙관적 락이 이 경우입니다.
비관적 락 - Pessimistic Lock
비관적락은 데이터를 조회하고 특정 작업을 할 때 테이블에 어떤 작업도 일어나면 안되는 상황에서 사용하기 적합하다고 볼 수 있습니다.
이는 데이터의 정확성과 일관성을 보장하는 방법이며, 이 방법은 테이블에 락을 거는 행위로 성능에 큰 영향을 끼칠 수 있습니다.
데이터베이스의 종류에 따라 락을 걸었으나 데이터가 조회되는 경우도 있으니 이는 어떤 데이터베이스를 사용하는지를 확인을 하고 잘 선택하기 바랍니다.
public interface LectureJpaRepository extends JpaRepository<LectureEntity, Long> {
//
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select l from LectureEntity l where l.id = :id")
Optional<LectureEntity> findByIdForUpdate(long id);
}
사용법은 위와 같이 조회할 때 @Lock 어노테이션을 통해 Lock을 시작하는 Start point를 설정해줍니다.
LockkModeType에는 아래와 같은 옵션들을 선택하여 적용할 수 있습니다.
LockModeType | Action |
READ | Entity의 버전을 확인합니다. - jpa1.0 버전 호환 |
WRITE | Entity가 변경이 되지 않더라도 버전을 자동으로 올려줍니다. - jpa1.0 버전 호환 |
OPTIMISTIC | thread가 종료될 때 Entity의 버전을 확인합니다. |
OPTIMISTIC_FORCE_INCREMENT | Entity가 변경이 되지 않더라도 버전을 자동으로 올려줍니다. |
PESSIMISTIC_READ | 데이터베이스가 지원하면 비관적락을, 지원하지 않다면 명시적 락으로 통해 읽기는 가능하지만 CUD는 할 수 없어집니다. |
PESSIMISTIC_WRITE | 해당 쓰레드를 제외 나머지 모든 쓰레드는 lock이 해제될때까지 Block됩니다. |
PESSIMISTIC_FORCE_INCREMENT | 비관적으로 잠기고, Entity가 변경되지 않더라도 버전이 자동으로 오릅니다. |
NONE | 잠금을 걸지 않습니다. |
public class LectureService {
@Transactional
public Lecture loadLecture(long lectureId) {
// 조회 - Lock시작
// 작업들
//종료 - Transaction이 종료될 때 Lock을 풉니다
}
}
비관적 락의 경우 Transaction내에서 유지되며, 해당 영역 내에서 작업을 끝내야 데이터의 일관성을 유지할 수 있습니다.
너무 많은 처리 및 Transaction전파에 의해 Lock이 길어질 수 있어 주의가 필요합니다.
낙관적 락 - Optimistic Lock
Version데이터를 통해 데이터의 일관성을 보장하는 방법입니다.
비관적락과 달리 테이블을 잠그거나 하지는 않지만, 버전이 다를 경우 데이터 수정작업 등을 처리하지 않는 것으로 동시성을 처리하였습니다.
비관적락에 비해 사용하기 쉽다는 장점이 있으며, 테이블을 잠그지 않기 때문에 성능적인 측면에서 더욱 효율적으로 관리할 수 있는 방법입니다.
단점으로는 버전을 통해 데이터의 일관성을 유지하기 때문에 동시에 여러번의 요청이 들어올 경우 많은 데드락이 발생 할 수 있습니다.
아래와 같은 방법으로 Entity에 @Version어노테이션을 사용함으로써 간단하게 명시 할 수 있습니다.
@Entity
public class LectureEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Version
private int version
}
버전의 타입으로는 int, Long, TimeStemp와 같은 자료를 사용할 수 있으며, TimeStemp의 경우 데이터의 일관성을 보장하기에는 int와 Long타입에 비해 부적절하지만, 상황에 따라 사용하는 경우가 존재한다고 합니다.
데이터베이스에서 락을 거는 만큼 무분별한 락사용과 제대로 된 설계가 아닐 경우 큰 성능적인 이슈를 야기할 수 있습니다.
이를 염두하고 적절한 락을 사용하는 것을 권장합니다.
또한 이 블로그글은 Hibernate구현체, Mysql기준으로 작성된 만큼 다른 데이터베이스, 또한 다른 기능들과 같이 사용할 경우 위에 소개한 상황과 다른 상황들이 발생할 수 있으니 확인해보시기 바랍니다.
Hibernate공식문서: https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#locking
'Java' 카테고리의 다른 글
[ThreadLocal] Thread영역에 데이터를 저장하고 싶어요 (1) | 2025.01.27 |
---|---|
오차 없는 실수 연산 어떻게 해야 할까? (0) | 2025.01.10 |
[Java] Static (3) | 2024.12.26 |
[ Basic ] I/O Stream이란? (0) | 2024.12.23 |
JPA는 어떤 기술일까? (0) | 2024.04.12 |