PLOD

[Spring] 재고시스템으로 알아보는 동시성 이유 본문

개발 공부/Spring framework

[Spring] 재고시스템으로 알아보는 동시성 이유

훌룽이 2024. 10. 3. 18:54

현업에서 이커머스 관련 프로젝트를 진행하려면 대규모 트래픽 대응, 마이크로서비스 운영, 캐시, 배치 작업 등 다양한 기술을 활용하여 상품의 선착순 구매를 효과적으로 지원하는 서비스를 만들어야 하는 상황이 생길 것이다.

상품의 선착순 구매 페이지는 많은 트래픽이 예상되며, 예상치를 상회하는 트래픽이 몰리는 상황을 가정한다.

실제 대규모 트래픽 자체를 다루는 기술은 클라우드 기술을 활용한 경우가 많아 이번 프로그램에서 다루기 어려워서, 이번에 내가 진행한 프로젝트에서는 대규모 트래픽과 분산서버 운영을 고려한 설계능력을 주로 다루고자 한다.

 

먼저 동시성 문제란, 동일한 하나의 데이터에 2 이상의 스레드, 혹은 세션에서 가변 데이터를 동시에 제어할 때 나타는 문제로,

하나의 세션이 데이터를 수정 중일때, 다른 세션에서 수정 전의 데이터를 조회해 로직을 처리함으로써 데이터의 정합성이 깨지는 문제를 말한다.

 

Redis 를 사용하여 동시성 문제를 해결하는 대표적인 라이브러리 2가지가 존재한다,

  1. Lettuce
  2. Redisson

1. Lettuce

  • Setnx 명령어를 활용하여 분산락을 구현 (Set if not Exist - key:value를 Set 할 떄. 기존의 값이 없을 때만 Set 하는 명령어)
  • Setnx 는 Spin Lock방식이므로 retry 로직을 개발자가 작성해 주어야 한다.
  • Spin Lock 이란, Lock 을 획득하려는 스레드가 Lock을 획득할 수 있는지 확인하면서 반복적으로 시도하는 방법이다.

 

📌 Spin Lock 과정

 

2. Redisson

  • Pub-sub 기반으로 Lock 구현 제공
  • Pub-Sub 방식이란, 채널을 하나 만들고, 락을 점유중인 스레드가, 락을 해제했음을, 대기중인 스레드에게 알려주면 대기중인 스레드가 락 점유를 시도하는 방식이다.,
  • 이 방식은, Lettuce와 다르게 대부분 별도의 Retry 방식을 작성하지 않아도 된다.

 

📌 Pub-Sub 과정

 

 

Lettuce와 Redisson 비교

  • Lettuce는 더 가볍고 순수한 Redis 클라이언트로 고성능 비동기 처리가 필요한 애플리케이션에 적합하다. 단순히 Redis 명령어를 실행하고 데이터를 읽고 쓰는 데 초점이 맞춰져 있다.
  • Redisson은 Redis를 분산 데이터 저장소로서 활용하려는 경우에 유리하며, 다양한 고급 데이터 구조와 분산 락 등의 기능을 제공한다. 더 많은 기능과 추상화가 제공되는 대신, 성능 면에서는 Lettuce보다 무거울 수 있다.

 

이에 MySQL의 데이터베이스의 자체에서는 Pessimistic Lock을 통해서 데이터베이스의 정합성을 맞춰추고 , Redis에서는  RLock을 통한 동시성 처리를 위한 재고 데이터 반영하도록 하여 결제 실패 및 동시 요청 시, 데이터베이스 정합성 유지하도록 하였다.

    @Transactional
    public void decreaseStock(Long productId, int quantity) {
        RLock lock = redissonClient.getLock(productId.toString());
        try {
            boolean isLocked = lock.tryLock(1000, 30, TimeUnit.SECONDS);
            if (isLocked) {
                Product product = productRepository.findProductById(productId).orElseThrow(RuntimeException::new);
                product.removeStock(quantity);
                productRepository.save(product);
            } else {
                System.out.println("상품 재고 수정 중입니다. --- (DB Locked)");
                return;
            }
        } catch (InterruptedException e) {
            throw new RuntimeException("Thread interrupted" + e);
        } finally {
            lock.unlock();
        }
    }
  • 10000명의 가상 사용자를 대상으로 재고가 10개인 제품에 한해서 부하테스트 실시

 

참고

https://www.inflearn.com/course/%EB%8F%99%EC%8B%9C%EC%84%B1%EC%9D%B4%EC%8A%88-%EC%9E%AC%EA%B3%A0%EC%8B%9C%EC%8A%A4%ED%85%9C

 

재고시스템으로 알아보는 동시성이슈 해결방법 강의 | 최상용 - 인프런

최상용 | 동시성 이슈란 무엇인지 알아보고 처리하는 방법들을 학습합니다., 동시성 이슈 처리도 자신있게! 간단한 재고 시스템으로 차근차근 배워보세요. 백엔드 개발자라면 꼭 알아야 할 동

www.inflearn.com

 

Comments