Spring 프로젝트

ShedLock vs Redisson

Paragon53 2025. 6. 16. 09:36

minecraft-font

#Redisson #ShedLock

💡 Spring 프로젝트: ShedLock vs Redisson

📘 기술 선택 정리

 @Scheduler를 이용해 평점을 업데이트하는 기능이며, 하루에 한번 특정 시간에 실행하는 기능이 있다.

 

 기능은 평점을 조회해서 Redis 서버에 저장했다가 특정 시간에 자동 갱신 업데이트를 진행하여 Redis에 있는 데이터를 다른 기능에서 빠르게 조회하여 실행하도록 하는 기능을 구현 중이다.

 

 

왜 하필 캐시 인가, 유저테이블을 이용할 수도 있지 않는가?

  1. 실시간 업데이트의 부담 감소

    > 유저 테이블에 접근해 평점 데이터를 가져오는 경우 실시간 업데이트가 되면 매번 리뷰 DB에서 평점의 평균을 계속 조회 및 계산해야 하므로
    트래픽이 급증할 때, 서버 부하가 심해진다.

  2. 캐시는 ‘읽기에 최적화된 구조’이다. 

       > 빨리 읽고 응답에 특화 되어 있어, 동시 다발적인 요청에 효율적으로 처리할 수 있다.

3. 유저 테이블은 쓰기 작업이 많고, 보안 이슈가 있음 

        > 유저테이블에는 개인정보등 민감한 데이터가 있기 때문이다.
           매번 캐시를 쓰지 않고 유저 테이블을 직접 건드리면 불필요한 조회/연산, 보안 위험  발생 할수 있다.

4. 캐시는 갱신 주기를 자유롭게 설정 할 수 있음.

         > 평점은 실시간으로 바뀔 필요가 없는 경우가 많다.   하루에 한 번이나 특정 시점에 평균 평점을 캐시에 갱신하면 충분하다.    

업데이트 할때 마다 실행되는 과정의 로직이다. 탈퇴 회원 여부를 확인 후, 업데이트가 가능한 상태인가를 확인한 후에 평점을 조회 Key-Value 형태로 Redis에 저장한다.

 

    if, 서버를 증성/확장한다면, 스케쥴러가 여러번 실행 할건데 이걸 여러번 반복이 아닌 단 한번,
            전 서버에서 발생하게 할것인가? 

     

 구글링 해봤을때, ShedLock과 RedissonLock이 있다.

  ShedLock RedissonLock
개요 ShedLock은 Spring Scheduler가 여러 서버에서 동시에 실행되는 것을 방지하는 도구이다. Redis를 이용해서 분산 락을 직접 관리 하는 방식
장점  DB 기반으로 락을 관리한다. 

 @scheduled 메서드에 손쉽게 적용이 가능하다.


 Redis나 Zookeeper 같은 외부 락 시스템
없이도 작동한다.

멀티 서버환경에 유용 

ShedLock은 시간 기반의 점유권(lock_until)
쓰는 구조
DB 테이블 필요없음

락 해제 실패나 네트워크 문제에 대비해서
    Wachdog 기능도있다.

일반 코드, 비스케줄러 코드에서 자유롭게 사용 가능.
단점 DB기반이기에 테이블 관리가 필요하다. 예기치 못한 상황을 대비한 추가 로직이 필요하다.

 

 

ShedLock 사용법

더보기

ShedLock 사용법

   1. 의존성 추가

 

// build.gradle
buildscript {
    ext {
        shedlockVersion = [ShedLock 버전 적용]
    }
}
// shedlock
implementation "net.javacrumbs.shedlock:shedlock-spring:${shedlockVersion}"
implementation "net.javacrumbs.shedlock:shedlock-provider-jdbc-template:${shedlockVersion}"

 

   2. 테이블 추가

CREATE TABLE shedlock (
    name VARCHAR(64),
    lock_until TIMESTAMP(3),
    locked_at TIMESTAMP(3),
    locked_by VARCHAR(255),
    PRIMARY KEY (name)
);

3. 동작

 저장된 이름의 락의 시도 -> lock_until이 현재시간보다 이전 상태라면 락이 가능 -> locked_at, locked_by,lock_until 값을 갱신
=> 언제까지 유효하고, 누가 잡았으며 언제 잡았는가를 기록에 남긴다.

 

 

4.삭제 / 해제
   삭제
       ShedLock은 처리가 끝나도 DB에서 레코드를 삭제 하지 않는다.

       대신 lock_until만 갱신하며 기록을 누적.

 

이름: updateTutorRatings
lock_until: 2025-06-17 05:01:00

    이 시간 전까지는 다른 서버가 락을 잡을 수 없다.


   해제

        명시적으로 해제 하지 않고   lock_until 시간이 지나면 → 자동으로 다시 락 시도 가능.  

 

참고

https://beaniejoy.tistory.com/56

 

[Spring] 이벤트 처리를 위한 스케줄러 작업 조정(ShedLock 활용)

사용 배경 문제 인식 - 1. outbox 테이블 전체 조회로 인한 문제 - 2. scale-out 상황에서 스케줄러 작업의 중복 실행의 가능성 문제 ShedLock을 위한 프로젝트 설정 ShedLock을 이용한 스케줄링 Lock 설정 - Sc

beaniejoy.tistory.com

 

 

 

Redisson 사용

더보기

Redisson 사용

key: ratingUpdateLock
value: UUID (고유 식별자)
TTL: 30초, 60초 등 사용자가 지정

 

 

    락을 획득한 서버는 이 key를 Redis에 세팅

    다른 서버는 이 키가 존재하면 락을 획득하지 못함

RLock lock = redissonClient.getLock("ratingUpdateLock");
try {
    if (lock.tryLock(10, 60, TimeUnit.SECONDS)) {
        // 락을 잡은 상태 (최대 60초 동안 유지)
        // 여기서 평점 계산 작업 수행
    }
} finally {
    lock.unlock(); // 락 해제
}

 

tryLock(10, 60, TimeUnit.SECONDS)

              최대 10초 동안 락을 기다리고,

              락을 잡으면 60초 동안 유지됨 (TTL)

✅ Watchdog 기능 (자동 연장)

 

             기본 lock.lock() 은 자동 연장 기능이 활성화됨 (30초 간격)

             만약 코드가 오래 걸리면 → 락이 풀리지 않도록 자동 갱신됨

            단, tryLock(timeout, leaseTime, unit) 은 수동 타이머이므로 자동 갱신 안 됨


✅ 사용 시 주의할 점

  • 주의사항설명

     

    반드시 try...finally 로 unlock() 해야 함 락이 남아있으면 다른 서버가 못 잡음
    락 실패 시 로직 설계 필요 락 못 잡았을 때 대기 or 무시? 결정해야 함
    Redis 장애 시 락 작동 안 함 Redis 클러스터 구성 추천
    락이 풀리지 않는 예외 대비 필요 TTL 설정 필수

    https://velog.io/@hgs-study/redisson-distributed-lock
 

Redisson 분산락을 이용한 동시성 제어

Redis 클라이언트인 Redisson 분산락(Distributed Lock)을 이용해서 동시성을 제어하는 포스팅을 진행해봤습니다 (예제 포함)

velog.io

 

상황 추천 락 방식  이유
Spring @Scheduled 작업만 단일 실행하고 싶다 ShedLock 간편하고 DB 기반
Redis를 이미 쓰고 있다 / DB를 락에 쓰고 싶지 않다 Redisson Redis에 상태 저장
락이 필요한 범위가 스케줄러뿐만 아니라 조회, 초기화 등 다양한 코드에서 필요하다 Redisson 더 유연함
락 획득/해제 로직을 커스터마이징 하고 싶다 Redisson tryLock 가능

 

 

자가 체크

  1. DB 테이블을 읽고 쓰기를 줄이기 위해 작업이다. ShedLock을 사용하면 목적이 애매해짐

 

  2. Redis로 만들면 추가 로직은 있지만 DB를 사용하지 않게 한다.

 

 

 

고민 끝에 네트워크 단절, 예기치 못한 에러로 서버 재시작, 지속적인 운영을 고려했을때, Reddison을 사용하기로 했다.