앞선 글에서는 Lettuce 기반의 RedisTemplate
과 RedisRepository
를 활용한 기본적인 Redis 사용법과 그 한계를 정리했다. 이번 글에서는 그 대안으로 자주 활용되는 Redisson
과 Lua 스크립트
조합을 소개한다.
이 조합은 특히 원자성 보장, 조건 분기, 복합 연산 처리가 필요한 경우 강력한 선택지가 된다. 실무에서 분산 락, RateLimit, 캐시 무효화 등 다양한 시나리오에 적용되는 구조이기도 하다.
RedisTemplate
이나 RedisRepository
는 단일 명령어 수준에서는 안전하지만, 여러 명령어를 조합해서 처리할 때는 Race Condition이 발생할 수 있다. 이를 해결하기 위해 Redis는 Lua 스크립트
를 통한 원자 명령 실행을 지원한다.
Lua는 if
, else
, for
같은 제어문을 사용 가능하다. 이를 통해 Java에서는 구현이 번거로운 조건 로직을 Redis 내부에서 직접 처리할 수 있다.
Redisson은 Redis 기반의 고급 클라이언트 라이브러리로, 다음과 같은 기능을 제공한다:
RLock
)RMapCache
, RLocalCachedMap
)RList
, RMap
, RSet
등)RScript.eval()
)Redisson은 단순히 Redis 명령을 보내는 도구가 아닌, 분산 환경에서 유틸리티 객체로 Redis를 사용하는 고급 API 집합이라 볼 수 있다.
Redisson은 다음과 같은 방식으로 Lua 스크립트를 실행한다.
val result = redissonClient.script.eval<Long>(
RScript.Mode.READ_WRITE,
luaScript, // 문자열로 된 Lua 코드
RScript.ReturnType.INTEGER,
listOf("ratelimit:ip:127.0.0.1:minute", "ratelimit:ip:127.0.0.1:blocked"),
limit.toString(),
now.toString(),
expireMillis.toString()
)
스크립트 실행은 단일 명령처럼 원자적으로 실행되며, 실행 도중 외부 간섭 없이 Redis에서 완결된다.
Lua는 Redis에 내장된 경량 스크립트 언어로, 다음과 같은 특징을 가진다:
KEYS
와 ARGV
배열을 통해 외부에서 전달된 값 접근redis.call()
을 통해 Redis 명령 호출 (예: SET
, ZADD
, ZREMRANGEBYSCORE
등)명령어 | 설명 |
---|---|
SET key value | 문자열 저장 |
GET key | 문자열 조회 |
EXPIRE key seconds | TTL 설정 |
ZADD key score member | 정렬된 집합에 멤버 추가 |
ZREMRANGEBYSCORE key min max | 정렬된 집합에서 범위 삭제 |
ZCARD key | ZSET 내 멤버 개수 조회 |
INCRBY key amount | 정수 증가 |
HSET key field value | 해시 필드 값 설정 |
HGET key field | 해시 필드 값 조회 |
DEL key | 키 삭제 |
LPUSH key value | 리스트 앞에 요소 추가 |
LPOP key | 리스트에서 요소 꺼내기 |
SADD key member | SET에 요소 추가 |
SCARD key | SET 크기 확인 |
이 명령들은 Java에서 여러 번 나누어 호출할 수도 있지만, Lua 내부에서 한 번에 실행하면 원자적으로 처리되므로 예기치 않은 Race Condition을 막을 수 있다.
Redis는 해당 Lua 스크립트를 실행할 때 모든 명령을 큐에 밀어넣고 한 번에 처리하기 때문에 중간에 다른 요청이 끼어들 수 없다. 이로 인해 높은 동시성 환경에서도 일관성을 유지할 수 있다.
Redisson + Lua 조합은 Lettuce 기반의 기본 API로는 구현이 어려운 고급 흐름 제어를 Redis 내부로 위임할 수 있게 해준다.
특히 분산 환경, 조건 분기, 원자성 보장이 중요한 서비스에서는 이 패턴이 사실상 표준처럼 자리잡고 있다.