[Spring] 동시성 문제 해결3: Redis💾

🙈·2024년 2월 5일
0

Redis를 통해 동시성 이슈 해결

🎤 대표적인 라이브러리

  1. Lettuce
    • setnx (set if not exist) 명령어를 활용하여 분산락 구현
      • key와 value를 set할 때, 기존의 값이 없을 때만 set을 진행
    • spin lock 방식
      • lock을 획득하려는 thread가 lock을 사용할 수 있는지 반복적으로 확인하면서 lock 획득
  1. Redisson
    • pub-sub 기반의 lock
      • 채널을 하나 만들고 락을 점유 중인 thread가 락을 해제할 때, 해당 lock을 획득하려고 대기 중인 thread에게 해제를 알려줌
      • lettuce와 다르게 락 획득 로직을 개발자가 작성할 필요 없음

🥬 Lettuce

Redis에서 setnx를 통해 키를 획득하는 방식이다.

적용 과정

  1. Redis dependency 추가
    implementation 'org.springframework.boot:spring-boot-starter-data-redis'

  2. Redis 명령어를 활용하기 위한 redis repository 생성

    public Boolean lock(Long key) {
      return redisTemplate
              .opsForValue()
              .setIfAbsent(generateKey(key), "lock", Duration.ofMillis(3_000));
    }
    
    public Boolean unlock(Long key) {
      return redisTemplate
              .delete(generateKey(key));
    }
    
    private String generateKey(Long key) {
      return key.toString();
    }
  3. lock의 획득과 해제를 위한 facade 정의

     public void decrease(Long id, Long quantity) throws InterruptedException {
       while (!redisLockRepository.lock(id)) {
         Thread.sleep(100);
       }
    
       try {
           stockService.decreaseStock(id, quantity);
       } finally {
           redisLockRepository.unlock(id);
       }
    }

장·단점

장점

  • 구현이 단순하다.
  • spring data redis를 이용하면 lettuce가 기본이므로 별도의 라이브러리를 사용할 필요가 없다.
    단점
  • spin lock 방식이므로 redis에 부하를 줄 수 있다.
    Thread.sleep()을 통해 락 획득 재시도에 텀을 주어야하므로

🚀 Redisson

Redis가 채널을 통해 통신하고 있는 것이다.

Redisson은 자신이 점유하고 있는 락을 해제할 때 채널에 메세지를 보내줌으로써 다른 thread에게 락을 획득하라고 전달한다.

적용 과정

  1. Redisson dependency 추가
    implementation 'org.redisson:redisson-spring-boot-starter:3.25.2'
    해당 의존성에서 lock과 관련된 라이브러리를 제공해주므로 별도의 repository를 작성할 필요가 없다.

  2. lock 획득과 해제를 위한 facade 정의

    RLock lock = redissonClient.getLock(id.toString());
    
    try {
      boolean available = lock.tryLock(10, 1, TimeUnit.SECONDS);
    
      if (!available) {
          System.out.println("lock 획득 실패");
          return;
      }
      stockService.decreaseStock(id, quantity);
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    } finally {
      lock.unlock();
    }

장·단점

장점

  • 라이브러리에서 락 획득 재시로를 기본으로 제공한다.
  • pub-sub 기반으로 redis의 부하를 줄여준다.

단점

  • 구현이 복잡하다.
  • 별도의 라이브러리 활용 필요하다.

⚖️ 실무에서의 활용 방안

Lettuce vs. Redisson

실무에서는 재시도의 필요성에 따라 혼용하여 사용한다.

재시도가 필요하지 않은 락 ⇒ lettuce
재시도가 필요한 경우 ⇒ redisson

MySQL vs. Redis

MySQL

  • 이미 MySQL을 사용하고 있다면 별도의 비용이 필요없다.
  • 어느 정도의 트래픽까지는 문제없이 활용이 가능하다.

Redis

  • 활용중인 Redis가 없다면 별도의 구축비용과 인프라 관리 비용이 발생한다.
  • MySQL보다 성능이 좋다.
profile
개발 일기🌱

0개의 댓글