( 이전글과 이어지는 포스트입니다. )
구매 서비스에서 재고 동시성 이슈 해결해보기(1) - JPA LOCK
4주 간 예약 구매 사이드 프로젝트를 진행했습니다. `예약 구매` 서비스는 특정 기념일이나 타임세일과 같이 해당 기간 또는 시간에만 상품을 사용자들에게 노출하고, 판매하는 이미 많은 브랜
devsting.tistory.com
'분산락'은 다중 서버 + 분산 DB 환경에서 동시성을 제어하기 위해 사용하는 Lock 기법입니다. 대표적으로 Mysql User Lock, Redis Lettuce, Redis Redisson이 있습니다. 반드시 Redisson을 사용할 필요는 없지만, 기본적으로 디스크를 사용하는 데이터베이스보다 메모리를 사용하는 Redis가 더 빠르게 락을 획득 및 해제할 수 있다는 점, Spin Lock을 사용하는 Lettuce보다 Pub/Sub으로 데이터 베이스 부하를 줄일 수 있다는 점에서 Redisson을 사용해보게 되었습니다.
개발 방법
Redisson 의존성 설정
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.redisson:redisson-spring-boot-starter:3.23.3'
코드 구현
락 범위가 트랜잭션 범위보다 크도록 @Transactional 어노테이션이 붙은 메서드를 감싸줍니다.
@Transactional
public void purchase(Long productId, Integer quantity) {
Stock stock = stockRepository.findByProductId(productId)
.orElseThrow(IllegalArgumentException::new);
stock.purchase(quantity);
}
public void purchase(Long productId, Integer quantity) {
RLock lock = redissonClient.getLock(String.format("purchase:product:%d", productId));
try {
boolean available = lock.tryLock(40, 1, TimeUnit.SECONDS);
if (!available) {
System.out.println("redisson getLock timeout");
throw new IllegalArgumentException();
}
stockService.purchase(productId, quantity);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
테스트 코드 구현
@Test
void 동시에_100명이_티켓을_구매한다() throws InterruptedException {
Long productId = productRepository.save(Product.of("티켓", 36_000, "공연 티켓", ProductType.NOT_RESERVATION, Stock.of(100)))
.getId();
ExecutorService executorService = Executors.newFixedThreadPool(100);
CountDownLatch countDownLatch = new CountDownLatch(100);
for (int i = 0; i < 100; i++) {
executorService.submit(() -> {
try {
stockService.purchase(productId, 1);
} finally {
countDownLatch.countDown();
}
});
}
countDownLatch.await();
Stock actual = stockRepository.findByProductId(productId)
.orElseThrow();
assertThat(actual.getStock().getRemain()).isZero();
}
TODO
동시성 이슈를 처리하기 위해 연산량이 많은 재고 사용량 DB자체를 Redis로 두고 전체 재고량은 RDB에서 관리하는 방법도 있습니다. 그러면 재고 사용량 데이터를 RDB에 어떻게, 잘 싱크할지가 중요한데 Redis를 더 학습하고 이 방법을 적용해 보도록 하겠습니다. 감사합니다.
선물하기 시스템의 상품 재고는 어떻게 관리되어질까? | 우아한형제들 기술블로그
{{item.name}} 안녕하세요. 저는 주문서비스팀의 서버개발자 강홍구입니다. 이 글에서는 배달의민족 선물하기 서비스의 상품권 재고관리를 위한 시스템 설계에 대한 경험을 공유드리고자 합니다.
techblog.woowahan.com
Redisson 참고 자료
풀필먼트 입고 서비스팀에서 분산락을 사용하는 방법 - Spring Redisson
어노테이션 기반으로 분산락을 사용하는 방법에 대해 소개합니다.
helloworld.kurly.com
'SideProject' 카테고리의 다른 글
스위프 5기 FADE 프로젝트 돌아보기 후기 (0) | 2024.08.12 |
---|---|
Sse 알림 기능 적용해보기 (Spring Boot) (0) | 2024.06.14 |
Redis Cache 적용해서 Tps를 높여보기 (2) | 2024.03.05 |
구매 서비스에서 재고 동시성 이슈 해결해보기(1) - JPA LOCK (0) | 2024.02.26 |