스프링 - Redission 분산락으로 동시성 문제 해결하기 예시
작성 일자 : 2024년 12월 13일
의존성 추가
dependencies {
implementation 'org.redisson:redisson-spring-boot-starter:3.40.1'
}
spring-boot-starter-data-redis
는redisson-spring-boot-starter
에 의존성을 가지고 있기 때문에 별도로 추가하지 않아도 됩니다.
프로퍼티 파일 작성
spring:
data:
redis:
host: ${REDIS_HOST}
port: 6379
password: ${REDIS_PASSWORD}
RedisConfig
클래스 작성
@Configuration
@RequiredArgsConstructor
public class RedisConfig {
@Value("${spring.data.redis.host}")
private String redisHost;
@Value("${spring.data.redis.port}")
private int redisPort;
@Value("${spring.data.redis.password}")
private String redisPassword;
/*
* Redisson Client
*/
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer().setAddress("redis://" + redisHost + ":" + redisPort);
config.useSingleServer().setPassword(redisPassword);
return Redisson.create(config);
}
}
분산락 사용하기
private static final String LOCK_KEY = "stock:";
private static final long WAIT_TIME = 10;
private static final long LEASE_TIME = 5;
...
// Lock 가져오기
RLock lock = redissonClient.getLock(LOCK_KEY + productId);
try {
// Lock 획득 시도
boolean locked = lock.tryLock(WAIT_TIME, LEASE_TIME, TimeUnit.SECONDS);
if (!locked) {
throw new RuntimeException("Lock 획득 실패");
}
// 임계 구역
Stock stock = stockRepository.findByProductId(productId)
.orElseThrow(() -> new RuntimeException("존재하지 않는 상품입니다"));
if (stock.getQuantity() < quantity) {
throw new RuntimeException("재고가 부족합니다");
}
stock.setQuantity(stock.getQuantity() - quantity);
stockRepository.save(stock);
return true;
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
// Lock 해제
lock.unlock();
}