레이스 컨디션이란?
멀티쓰레드 환경에서 공유자원을 경합하며 벌어지는 동기화 문제가 발생한 현상.
레디스의 INCR 활용한 간단한 해결방안
INCR은 Redis에서 제공하는 명령 중 하나로, "increment"의 약어이다. 이 명령은 키에 저장된 값이 정수일 경우에만 사용할 수 있다. 키에 저장된 값을 1씩 증가시킨다.
SET mykey 10
INCR mykey
위 예제에서 mykey의 초기 값은 10으로 세팅했다. INCR mykey를 실행하면 mykey의 값이 1이 증가하여 11이 된다.
이 명령은 분산 환경에서도 안전하게 사용할 수 있어서,
여러 클라이언트가 동시에 INCR을 실행해도 서로 간섭 없이 값이 증가한다
코드를 통해 알아보자
@Repository
public class RedisRepository {
private final RedisTemplate<String,String> redisTemplate;
public CouponCountRepository(RedisTemplate<String, String> redisTemplate) {
this.redisTemplate = redisTemplate;
}
public Long increment(){
return redisTemplate
.opsForValue()
.increment("coupon_count"); // incr coupon_count
}
}
Service로직
/**
* 레디스의 incr을 활용한 레이스컨디션 해결
* @param userId
*/
public void apply(Long userId){
long count = redisRepository.increment();
if(count > 100){
return;
}
couponRepository.save(new Coupon(userId));
}
테스트
@Test
public void 쿠폰100개까지만받기() throws InterruptedException {
int threadCount = 1000;
ExecutorService executorService = Executors.newFixedThreadPool(32);
CountDownLatch latch = new CountDownLatch(threadCount);
for(int i=0; i< threadCount; i++){
long userId = i;
executorService.submit(()->{
try{
applyService.apply(userId);
}finally {
latch.countDown();
}
});
}
latch.await();
long count = couponRepository.count();
assertThat(count).isEqualTo(100);
}
레디스의 INCR명령어를 활용한 레이스컨디션 해결방법을 알아보았다.
폭발적인 트래픽이 발생한다면 이 방법은 에러를 내게된다.
이럴때는 또 다른 해결방안인 카프카를 활용하는 방법, 락을 사용하는 방법 등이 있다.