setnx
(set if not exist) 명령어를 활용하여 분산락 구현Redis에서 setnx
를 통해 키를 획득하는 방식이다.
Redis dependency 추가
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
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();
}
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);
}
}
장점
Thread.sleep()
을 통해 락 획득 재시도에 텀을 주어야하므로Redis가 채널을 통해 통신하고 있는 것이다.
Redisson은 자신이 점유하고 있는 락을 해제할 때 채널에 메세지를 보내줌으로써 다른 thread에게 락을 획득하라고 전달한다.
Redisson dependency 추가
implementation 'org.redisson:redisson-spring-boot-starter:3.25.2'
해당 의존성에서 lock과 관련된 라이브러리를 제공해주므로 별도의 repository를 작성할 필요가 없다.
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();
}
장점
단점
실무에서는 재시도의 필요성에 따라 혼용하여 사용한다.
재시도가 필요하지 않은 락 ⇒ lettuce
재시도가 필요한 경우 ⇒ redisson
MySQL
Redis