여러 트랜잭션이 동시에 동일한 데이터에 접근할 때, 데이터 무결성과 일관성을 보장하기 위한 동시성 제어 메커니즘
데이터베이스나 애플리케이션에서 트랜잭션 간 충돌을 방지하기 위해 사용된다.
데이터베이스는 동시에 여러 클라이언트의 요청을 처리한다.
만약 두 트랜잭션이 같은 데이터를 동시에 수정하려 한다면, 다음과 같은 문제가 발생할 수 있다.
이러한 문제를 방지하기 위해 락을 통해 접근을 제어한다.
특징
대표 사례: JPA의 @Version 사용
@Entity
public class Product {
@Id
@GeneratedValue
private Long id;
private String name;
private int stock;
@Version
private int version;
}
JPA는 엔티티의 @Version 필드를 자동으로 관리하며, 업데이트 시 다음과 같은 쿼리를 수행한다.
update product
set stock = ?, version = version + 1
where id = ? and version = ?
version이 일치하지 않으면 OptimisticLockException이 발생하여 트랜잭션 충돌을 감지할 수 있다.
특징
대표 사례: SQL의 SELECT ... FOR UPDATE
JPA에서도 @Lock 어노테이션을 이용하여 비관적 락을 설정할 수 있다.
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select p from Product p where p.id = :id")
Product findByIdForUpdate(@Param("id") Long id);
이는 다음 SQL과 동일한 효과를 낸다.
select * from product where id = ? for update;
JPA는 표준적으로 다음의 LockModeType을 제공한다.
| LockModeType | 설명 |
|---|---|
| OPTIMISTIC | 버전 필드를 사용해 낙관적 락을 적용. 트랜잭션 커밋 시 버전 검증 |
| OPTIMISTIC_FORCE_INCREMENT | 낙관적 락과 동시에 버전 값을 강제로 증가시킴 |
| PESSIMISTIC_READ | 다른 트랜잭션의 쓰기 차단, 읽기는 허용 |
| PESSIMISTIC_WRITE | 읽기와 쓰기 모두 차단 (SELECT FOR UPDATE) |
| PESSIMISTIC_FORCE_INCREMENT | 비관적 락과 동시에 버전 증가 |
| NONE | 락을 사용하지 않음 |
Lock은 동시성 문제를 해결하기 위한 핵심 메커니즘으로, 시스템의 특성과 트래픽 패턴에 따라 낙관적 락과 비관적 락을 적절히 선택해야 한다.
낙관적 락은 높은 성능과 확장성을, 비관적 락은 강력한 일관성을 제공한다.
JPA는 이를 추상화하여 어노테이션과 LockModeType을 통해 쉽게 사용할 수 있도록 지원하므로, 도메인 로직의 성격에 맞는 락 전략을 선택하는 것이 중요하다.