
LOCK
다량의 접근이 예상되는 서비스의 경우엔
데이터 관리에 있어 동시에 접근하는 경우를 고려해야 한다
데이터의 일관성이 깨지거나 (중복 발행 등), 한정된 데이터지만
초과될 수 있기 때문이다
DB Lock 과 똑같은 개념이지만 적용법이 JPA다 보니 소개하게 되었다
낙관적 락 (Optimistic Lock)
데이터 변경시의 충돌을 감지하는 하여 충돌이 발생하면 그 때마다 재시도 하는 방식
@Version 어노테이션을 통해 버전 번호를 붙여 충돌 여부를 판단
실패 해도 되는 비즈니스 로직에 사용
실질적으로 DB 락을 걸지 않아 성능이 보장되나 충돌이 잦을 경우엔 오히려 성능이 하락 (계속되는 재시도)
@Entity
@Table(name = "order_sequence")
public class OrderSequence {
@Id
private LocalDate seqDate;
private Integer currentSeq;
@Version // 낙관적 락
private Long version;
public void incrementSeq() {
this.currentSeq++;
}
}
재시도 코드가 반드시 포함되어야함
@Service
@RequiredArgsConstructor
public class OrderService {
private final OrderSequenceRepository sequenceRepository;
@Transactional
public String generateOrderNo() {
int retryCount = 0;
int maxRetries = 3;
while (retryCount < maxRetries) {
try {
LocalDate today = LocalDate.now();
OrderSequence sequence = sequenceRepository
.findById(today)
.orElse(new OrderSequence(today, 0));
sequence.incrementSeq();
sequenceRepository.save(sequence); // 여기서 버전 체크
return String.format("ORDER-%s-%04d",
today.format(DateTimeFormatter.ofPattern("yyyyMMdd")),
sequence.getCurrentSeq()
);
} catch (OptimisticLockException e) {
// 재시도 코드
retryCount++;
if (retryCount >= maxRetries) {
throw new ServiceErrorException("주문번호 생성 실패");
}
// 잠시 대기 후 재시도
Thread.sleep(50);
}
}
throw new ServiceErrorException("주문번호 생성 실패");
}
}
비관적 락 (Pessimistic Lock)
데이터의 충돌이 날 것이라고 가정하고 데이터를 락 후에 해제하며 순차 처리 하는 방식
실패하면 안되는 비즈니스 로직에 사용
락을 걸고 있어 성능이 느릴 수 있음
PESSIMISTIC_READ, 공유 락
다른 쓰레드가 읽기만 가능, 쓰기는 불가
PESSIMISTIC_WRITE, 배타 락
다른 쓰레드가 읽거나 쓰는 것 자체가 불가능
한 쓰레드가 완전히 독점하는 방식
@Repository
public interface OrderSequenceRepository extends JpaRepository<OrderSequence, LocalDate> {
@Lock(LockModeType.PESSIMISTIC_WRITE) // 비관적 락
@Query("SELECT s FROM OrderSequence s WHERE s.seqDate = :date")
Optional<OrderSequence> findBySeqDateWithLock(@Param("date") LocalDate date);
}
Share article