I. 파이프라인
필요성
Redis는 기본적으로 요청-응답 구조이기 때문에 여러 명령을 순차적으로 실행하면 RTT(Round Trip Time) 오버헤드가 발생함
파이프라인은 이러한 불필요한 왕복 비용을 줄여 다수의 명령을 빠르게 처리할 수 있도록 도와줌
문제점
명령 간 의존성이 있을 경우 처리 순서에 따른 예외가 발생할 수 있고, 응답이 몰려오기 때문에 디버깅이나 실패 추적이 어려움
또한 원자성은 보장되지 않음
1. 파이프라인(Pipelining)이란?
- 여러 Redis 명령을 클라이언트에서 한꺼번에 전송하고, 서버는 응답을 순차적으로 반환
- 각 명령을 RTT(Round Trip Time)마다 보내지 않고, 한 번에 요청하고 한 번에 응답 받음
- 성능 최적화 측면에서 매우 유용 (컨텍스트 스위칭, 시스템 콜 감소)
(1) 특징
- 명령어는 Redis 서버에서 순서대로 처리됨
- 파이프라인 사용 시에도 Redis는 여전히 싱글스레드로 처리하므로 동시성 문제가 발생하지 않음
- 단, 명령어 간 의존성(예: SET 후 GET) 이 있는 경우에는 적절히 분리 필요
1) 실행 예시
- redis-cli --pipe 명령어나 Netcat(nc)을 통해 사용 가능
- 예: 100개의 INCR 명령을 하나의 파이프로 보내는 스크립트를 작성하면 매우 빠르게 처리됨
- redis-benchmark에서도 pipeline 설정 가능 (-P 옵션)
- 파이프라인은 트랜잭션과 다르게 원자성 보장 X, 하지만 병렬 처리보다 빠름
II. 루아
필요성
복잡한 조건 분기나 여러 명령어를 조합해 실행할 때 클라이언트-서버 간 여러 번 왕복하면 성능 저하 발생
Lua 스크립트를 이용하면 복잡한 로직을 Redis 서버 내에서 원자적으로 실행할 수 있어 효율적임
문제점
스크립트가 무한 루프에 빠지면 Redis 서버 전체가 영향을 받을 수 있으며, 샌드박스 환경의 제한으로 Lua 내 일부 기능은 사용 불가
또한 복잡한 로직은 유지보수와 디버깅이 어려울 수 있음
1. 이페머럴 스크립트
- 레디스 7.0 이전
- 임시 Lua 스크립트를 EVAL 명령으로 실행
- 스크립트는 매번 문자열로 전달되며, 매번 실행됨
- 예시
EVAL "return redis.call('GET', KEYS[1])" 1 mykey
- KEYS, ARGV 배열로 외부 인자를 전달받음
- 실행은 원자적으로 처리되며, 중간에 다른 명령이 끼어들 수 없음
(1) 데이터 저장 + TTL 설정 (원자적으로)
HMSET은 TTL을 지정할 수 없으므로 Lua로 해결
EVAL "
redis.call('HMSET', KEYS[1], ARGV[1], ARGV[2])
redis.call('EXPIRE', KEYS[1], ARGV[3])
" 1 user:1 age 30 86400
한 번의 명령으로 데이터 저장과 TTL 설정을 원자적으로 처리
(2) 여러 키에 값 저장 + 서로 다른 TTL 부여
EVAL "
redis.call('SET', KEYS[1], ARGV[1])
redis.call('EXPIRE', KEYS[1], ARGV[2])
redis.call('SET', KEYS[2], ARGV[3])
redis.call('EXPIRE', KEYS[2], ARGV[4])
" 2 k1 k2 v1 60 v2 120
MSET과 달리 각 키마다 TTL을 다르게 설정
(3) 조건 분기 + TTL
EVAL "
if redis.call('EXISTS', KEYS[1]) == 0 then
redis.call('SET', KEYS[1], ARGV[1])
redis.call('EXPIRE', KEYS[1], ARGV[2])
return 'OK'
else
return 'EXISTS'
end
" 1 session:user abc123 3600
조건부 저장 + TTL, 분기 로직을 Redis 서버 안에서 실행
(4) 복잡한 계산 로직 실행
EVAL "
local sum = 0
for i = 1, tonumber(ARGV[1]) do sum = sum + i end
return sum
" 0 10
(5) SCRIPT LOAD + EVALSHA
SCRIPT LOAD "return {KEYS[1], ARGV[1]}"
→ SHA1 반환 후 EVALSHA로 재사용
2. 레디스 함수 (Redis Functions)
(1) 개요
- Redis 7.0부터 도입된 기능으로,
EVAL
의 단점을 보완 - 스크립트를 Redis 서버에 등록해 재사용 가능
- 서버 재시작 후에도 유지되며, 보안·성능·관리성 향상
- 함수는 라이브러리 단위로 그룹화되어 관리됨
1) 등록 방법
FUNCTION LOAD "<Lua 스크립트 문자열>"
-- Lua 예시
redis.register_function{
function_name = 'echo_key',
callback = function(keys, args)
return keys[1]
end
}
2) 호출 방법
FCALL echo_key 1 mykey
FCALL <함수명> <numkeys> <key1> <key2> ... <arg1> <arg2> ...
3) 관리 명령어
명령어 | 설명 |
---|---|
FUNCTION LIST |
등록된 함수 목록 확인 |
FUNCTION DELETE <라이브러리> |
라이브러리 단위 삭제 |
FUNCTION FLUSH |
전체 삭제 |
FUNCTION DUMP / RESTORE |
백업 및 복구 가능 |
4) 특징 요약
- 등록된 함수는 재사용 가능하고, 서버가 재시작돼도 지속됨
- KEYS / ARGV 인자 구조는 EVAL과 동일
- 함수는 라이브러리 단위로 등록되며, 각각의 함수는
callback
으로 정의됨 - 샌드박스 환경에서 실행되므로 외부 접근 제한됨
5) EVAL과의 차이점
항목 | 이페머럴 스크립트 (EVAL) | Redis 함수 (FUNCTION) |
---|---|---|
실행 방식 | 매번 코드 전송 | 등록 후 함수명으로 호출 |
유지성 | 휘발성 | 영속성 |
적합 용도 | 단발 실행 | 반복 호출 |
관리 기능 | 없음 | LIST, DELETE 등 제공 |
3. 레디스의 루아 프로그래밍
(1) 개요
- Redis에서 Lua 스크립트는
redis.call()
또는redis.pcall()
로 Redis 명령을 호출 - 외부에서 전달된 값은
KEYS[]
와ARGV[]
배열을 통해 접근 - Lua는 가볍고 간결한 스크립트 언어로, Redis 서버 내에서 원자적 로직 실행에 적합
1) 명령 실행 방식
redis.call()
- Redis 명령 실행, 실패 시 에러 반환 → 스크립트 중단
redis.pcall()
- Redis 명령 실행, 실패 시 nil 또는 오류 메시지 반환 → 스크립트 계속 진행
예:
local exists = redis.call('EXISTS', KEYS[1])
local res = redis.pcall('GET', 'not-exist-key')
2) 변수 사용 및 스코프
- Lua는 기본적으로 전역 변수로 선언됨
- 반드시
local
키워드로 지역 변수로 지정해야 안전
예:
local count = redis.call('GET', KEYS[1])
3) 흐름 제어
- 조건문
if redis.call('EXISTS', KEYS[1]) == 0 then
redis.call('SET', KEYS[1], ARGV[1])
end
- 반복문
local sum = 0
for i = 1, tonumber(ARGV[1]) do
sum = sum + i
end
return sum
4) 반환 값 처리
return
을 사용하여 단일 값, 배열, 테이블 등 반환 가능
예:
return {KEYS[1], ARGV[1]}
5) 주의 사항
- Redis는 Lua 5.1 버전을 사용 → 최신 Lua 기능 일부 없음
- 스크립트에서 Redis 외부 네트워크 접근 불가 (샌드박스 환경)
- 무한 루프, 너무 긴 실행 시간 금지 → Redis 전체가 블로킹됨
- 반환 값은 반드시 Redis가 직렬화 가능한 타입이어야 함 (문자열, 숫자, 리스트 등)
III. 트랜잭션
1. 트랜잭션이란?
- Redis의 트랜잭션은 여러 명령을 묶어서 순차적으로 실행하는 기능
MULTI
로 트랜잭션 시작 → 명령들을 큐에 저장 →EXEC
로 실행- 기본적으로 원자성은
EXEC
단위에서만 적용됨
(1) 트랜잭션 명령어 구조
MULTI
SET key1 value1
INCR counter
EXEC
MULTI
: 트랜잭션 시작EXEC
: 큐에 저장된 명령을 순차적으로 실행DISCARD
: 트랜잭션 취소 (큐 비움)
1) 특성
- Redis는 명령어들을 그대로 순서대로 실행함
- 트랜잭션 실행 중 일부 명령이 실패해도 롤백되지 않음
→ 실패한 명령은 에러, 나머지는 실행됨 - 예외:
WATCH
를 사용한 낙관적 락이 있는 경우, 조건 불충족 시 전체EXEC
이 실행되지 않음
2) 낙관적 락 (WATCH
)
WATCH
는 지정한 키가EXEC
전까지 변경되지 않으면 실행- 키가 바뀌면
EXEC
자체가 무시됨 (abort) - 낙관적 락 (CAS, Compare-And-Set)과 유사한 동작 방식
예:
WATCH balance
MULTI
DECR balance
EXEC
→ balance
키가 다른 클라이언트에 의해 변경되면 EXEC
은 실행되지 않음
3) 사용 시 주의점
- Redis는 트랜잭션 중간에 명령을 실행하지 않기 때문에, 중간 결과 확인 불가
- 모든 명령은
EXEC
시점에 한꺼번에 실행되며, 실패해도 부분 실행이 남을 수 있음 - 복잡한 조건 분기, 반복문, TTL 설정 등은 불가능
→ 이 경우 루아 스크립트 사용 권장
4) 트랜잭션 vs 루아 비교
항목 | 트랜잭션 (MULTI/EXEC) | 루아 스크립트 (EVAL) |
---|---|---|
실행 방식 | 명령 큐 저장 후 실행 | Lua로 전체 로직 실행 |
조건 분기 | 불가능 | 가능 |
부분 실패 시 롤백 | 안 됨 | 안 됨 (그러나 원자적) |
사용 용도 | 단순한 명령 묶음 | 복잡한 로직, 조건 처리 |
IV. 모듈
1. 모듈이란?
- Redis는 기본적으로 제공되는 명령어와 자료형 외에, C 언어 기반 사용자 정의 확장 기능을 제공함
- 이를 모듈(module)이라 하며, 새로운 자료구조, 명령어, 기능을 직접 구현해 Redis 서버에 로드할 수 있음
- Redis 4.0 이상에서 지원
(1) 모듈로 구현할 수 있는 것
- 새로운 자료형 (예: Bloom Filter, Time Series 등)
- 새로운 명령어 (예:
my.hello
,ts.insert
,bf.add
) - Redis 클러스터 연동, 자동 만료 로직 등 내부 동작 확장
1) 모듈 작성 개요
- C 언어로 작성
- Redis에서 제공하는 API 사용 (
RedisModule_Init
,RedisModule_CreateCommand
, 등) .so
공유 라이브러리로 컴파일 후 Redis에 로드
2) 로드 방법
MODULE LOAD /path/to/mymodule.so
- Redis 시작 시 자동 로드하려면 redis.conf에
loadmodule
옵션 추가 - 로드된 모듈은 Redis 내부 명령처럼 동작
3) 예시: 단순 명령어 출력
int HelloCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
return RedisModule_ReplyWithSimpleString(ctx, "Hello from module!");
}
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
RedisModule_Init(ctx, "mymodule", 1, REDISMODULE_APIVER_1);
RedisModule_CreateCommand(ctx, "mymodule.hello", HelloCommand, "readonly", 0, 0, 0);
return REDISMODULE_OK;
}
- 이 코드는
mymodule.hello
라는 커맨드를 만들어, 호출 시 "Hello from module!"을 반환 - 실전에서는 RedisTimeSeries, RedisBloom, RedisSearch 같은 복잡한 기능도 모듈로 구현됨
4) 주의 사항
- Redis 프로세스 내에서 실행되므로 심각한 버그는 서버 전체에 영향을 줄 수 있음
- C로 직접 구현해야 하므로 진입 장벽이 있음
- 성능과 안정성 테스트가 중요
5) 실무 활용 예시
대표 모듈 | 기능 |
---|---|
RedisBloom | Bloom Filter, Count-Min Sketch 등 |
RedisTimeSeries | 시계열 데이터 저장/집계 |
RedisSearch | 텍스트 검색, 인덱스 |
RedisJSON | JSON 구조 저장 및 조회 |
V. 키 공간 알림
1. 키 공간 알림이란?
- Redis에서 특정 키에 대해 발생하는 이벤트(예: 설정, 삭제, 만료 등)를 Pub/Sub 메시지 형태로 알림해주는 기능
- 일반적으로 클라이언트가 특정 키의 변화를 실시간으로 감지하거나 동기화할 때 사용됨
- Redis 2.8부터 지원
(1) 동작 방식
- Redis는 특정 키의 이벤트 발생 시 특수 채널에 메시지 발행
- 클라이언트는 해당 채널을
SUBSCRIBE
또는PSUBSCRIBE
로 구독 - 알림 채널은 두 종류
__keyspace@<db번호>__:<key>
→ 명령 이름을 발행 (예: "set", "del")__keyevent@<db번호>__:<event>
→ 이벤트가 발생한 키를 발행
예:
PSUBSCRIBE __keyevent@0__:expired
→ DB 0에서 만료된 키의 이름을 실시간 수신
1) 설정 방법
- 기본적으로 Redis는 키 공간 알림을 비활성화함
- 다음과 같이 설정해야 함 (런타임/설정파일 모두 가능)
CONFIG SET notify-keyspace-events Ex
- 옵션 조합 예시
K
→ Keyspace notificationE
→ Keyevent notificationg
→ generic (set, del 등)x
→ expiredA
→ all
2) 활용 예시
- 만료된 세션 캐시 감지 후 정리
expired
이벤트로 세션 만료 알림을 수신해 후처리
- 키 변경 감지
set
,del
,rename
등의 이벤트를 구독하여 실시간 반영
- 분산 캐시 무효화
- 여러 노드 간 캐시를 일관되게 유지하려고 알림 기반 invalidation 처리
3) 주의 사항
- 키 이벤트는 Pub/Sub 기반이라 클라이언트가 반드시 구독 중이어야 함
- 과도한 이벤트 설정은 성능 저하 유발 가능 (예: A 옵션)
- 일시적인 네트워크 오류로 이벤트를 놓칠 수 있음 → 신뢰성 보장되지 않음
VI. 클라이언트 측 캐시
1. 클라이언트 측 캐시란?
- Redis 6부터 도입된 기능으로, 클라이언트가 Redis 데이터를 로컬에 캐싱하고, 서버에서 변경 알림을 받아 자동 무효화(invalidation) 하는 방식
- 기존에는 클라이언트 캐시가 수동 갱신 또는 TTL 기반이었지만, 이 기능을 통해 서버 주도 캐시 무효화가 가능해짐
(1) 동작 방식
- 클라이언트가
CLIENT TRACKING
명령으로 추적 모드를 활성화 - 이후 읽은 키들에 대해 Redis가 변경 이벤트 발생 시 무효화 메시지를 전송
- 클라이언트는 해당 키의 로컬 캐시를 무효화하거나 재요청
1) 기본 사용 방식
CLIENT TRACKING ON
GET mykey
→ 이후 mykey
가 변경되면 Redis는 클라이언트에게 invalidation 메시지를 전송
- 클라이언트는 이를 기반으로 로컬 캐시 무효화 처리
2) 주요 옵션
옵션 | 설명 |
---|---|
ON |
추적 활성화 |
BCAST |
모든 키에 대해 브로드캐스트 모드 사용 |
REDIRECT <id> |
지정한 Pub/Sub 클라이언트 ID로 무효화 전송 |
PREFIX <prefix> |
특정 접두어 키만 추적 |
NOLOOP |
자기 자신의 명령으로 인한 무효화는 제외 |
예:
CLIENT TRACKING ON BCAST PREFIX foo: NOLOOP
3) 활용 사례
- API Gateway 캐싱
- 자주 조회되는 값을 Redis에서 조회 후 클라이언트에 캐시하고, 서버에서 실시간 무효화 전송
- 게임 서버나 실시간 UI 캐시
- 플레이어 상태, 랭킹 등 조회 수요가 높은 데이터에 적용
- 분산 캐시 무효화 대체재
- 키 공간 알림보다 정밀하고, 클라이언트 단위로 분리 가능
4) 주의 사항
- 클라이언트가 무효화 메시지를 직접 처리해야 하므로 구현 복잡도 있음
- 무효화 메시지를 받지 못하면 오래된 캐시가 유지될 수 있음
- BCAST는 모든 키 변경을 브로드캐스트하므로 과도하게 설정 시 성능 저하 유발 가능
'서적 > 실전 Redis' 카테고리의 다른 글
CHAPTER 06 트러블슈팅 [PART 02 실전] (1) | 2025.07.01 |
---|---|
CHAPTER 05 레디스 운용 관리 [PART 02 실전] (0) | 2025.06.24 |
Chapter 02.자료형과 기능 [Part1 기초] (0) | 2025.05.26 |
Chapter 01.레디스의 시작 [Part1 기초] : 연습문제 포함 (1) | 2025.05.17 |