동시성 이슈를 해결하기 위한 Redisson 분산 락(Lock) 사용해보기
Redisson ?
- Java 기반의 분산 데이터 처리와 캐싱 솔루션을 제공하는 오픈 소스 라이브러리
- Redis를 기반으로 동작하며 Redis에 접근하기 위한 클라이언트 제공
- Redisson 에서 제공하는 분산 데이터 구조를 활용하여 데이터 처리 및 캐시 관리가 가능
Redisson 분산 락(Lock) 사용은 왜 ?
데이터의 일관성과 무결성을 보장하기 위해 여러 애플리케이션 인스턴스나 스레드를 동일한 자원에 동시에 접근하지 못하게끔 하여 동시성 제어로 사용
Setting 🔍
[ springboot 환경 내에서 처리 ]
1. Gradle 의존성 추가
- Spring Boot와의 통합을 위한 스타터
- Spring Data Redis와 쉽게 통합 가능
- Redis 연결 설정이 간단
- Spring 환경에서 사용할 경우 반드시 추가해야 할 의존성
# build.gradle
dependencies {
implementation 'org.redisson:redisson-spring-boot-starter:3.16.3'
}
2. Redis 서버와 연결 설정
# application.yml or application.properties
spring:
redis:
host: 127.0.0.1
port: 6379
password: # Redis 비밀번호 (생략 가능)
3. Redisson Client를 spring boot에서 사용할 수 있도록 Bean 등록
📌 회사에서 쓴 Redisson Client는 서비스 내에서 Bean으로 등록하지 않고 RedissonClient 을 주입 받아 사용하는 중이라 해당 방식대로 적을 예정입니다.
📌 service 로직 내에 private final RedissonClient redissonClient 선언 후 주입 받아 사용
그러나, Spring 컨텍스트에서 관리되도록 @Bean으로 등록 후 주입 받는 방식이 더 적합 !
@Override
public ResponseModel<ProcessorResponseDto> ProcessLock(ProcessorRequestDto dto) {
// RLock(기본 락) 사용
RLock lock = redissonClient.getLock("processorLock-" + idx);
ProcessorResponseDto responseDto = new ProcessorResponseDto();
try {
// 락 획득 성공
// 4초 락 대기 시간, 4초 락 획득 후 자동 해제 시간
boolean isLocked = lock.tryLock(4, 4, TimeUnit.SECONDS);
if(!isLocked){
// 락 획득에 실패했으므로 예외 처리
}
return responseDto;
} catch (InterruptedException e) {
// 쓰레드가 인터럽트 될 경우의 예외 처리
logger.error(Alarm.ALARM_ERROR + e);
responseDto.setRspMessage("FAIL");
return responseDto;
} finally {
// 락 해제 로직은 여기서 처리
// 락이 해제가 안되는 경우를 방지하기 위해 조건 처리 한 번 더 진행
if(lock.isLocked() && lock.isHeldByCurrentThread()) {
// 락 해제
lock.unlock();
}
}
}
- RLock : Redisson 분산 락 객체, 락 획득 및 해제 역할
- tryLock(long waitTime, long leaseTime, TimeUnit unit) : 락 획득 시 제한 시간 설정 / 데드락 방지에 용이 / Redis TTL 기능 활용으로 락을 자동으로 해제 할 수 있도록 시간 설정 가능 / true 는 성공, false 는 실패
Redisson 적용 후 🧐
Redisson을 적용 후 느낀 점으로, 내부 비즈니스 로직은 제외한 구조는 이렇게 구현했으며 이를 통해 한 테이블 내 데이터를 조회, 생성, 수정하는 과정에서 발생하는 동시성 이슈를 원만히 완화할 수 있었습니다.
Redisson의 동기화 메커니즘을 활용해 일부 동시성 문제를 해결했지만, 전체적인 동시성 제어는 시스템 설계와 구현 방식에 따라 달라지기 때문에 모든 동시성 이슈를 완벽히 해결할 수는 없었습니다.
예를 들어, 락 경합이 많거나 락 획득 대기 시간이 길어지는 경우, 동시에 작업할 수 있는 스레드 수가 한정적일 때 병목 현상이 발생하기도 했습니다. 이는 Redisson 적용 후에도 여전히 주의가 필요한 부분이었습니다.
그럼에도 불구하고, 분산 환경에서 자주 갱신되는 데이터를 처리하는 시스템에서 Redisson을 도입하여 발생했던 중복 수정, 데이터 불일치 문제와 같은 빈번한 이슈를 효과적으로 감소시킬 수 있었습니다.