Redis 활용 사례: 캐싱, 큐, 속도 제한
Redis를 스택에 추가했지만 단순한 키-값 저장소로만 사용하고 있나요? Redis는 캐싱, 큐잉, 속도 제한을 우아하게 해결할 수 있는 강력한 데이터 구조와 명령어를 제공합니다. 이 가이드에서는 이러한 패턴을 효과적으로 구현하는 방법을 보여줍니다.
이러한 사용 사례에 Redis를 사용하는 이유
Redis는 속도와 다양성으로 유명한 인메모리 데이터 저장소입니다. 문자열, 해시, 리스트, 세트, 정렬된 세트 등을 지원합니다. 원자적 연산과 내장 만료 기능 덕분에 캐싱, 큐, 속도 제한에 이상적입니다. 또한 모든 언어와 쉽게 통합할 수 있습니다.
Redis를 사용한 캐싱
캐싱은 가장 일반적인 Redis 사용 사례입니다. 자주 액세스하는 데이터를 Redis에 저장하여 데이터베이스 부하를 줄이고 응답 속도를 높입니다.
기본 캐시-어사이드 패턴
캐시-어사이드 패턴에서는 애플리케이션이 먼저 Redis를 확인합니다. 데이터가 없으면(캐시 미스) 데이터베이스에서 가져온 후 다음을 위해 Redis에 저장합니다.
import redis
import json
r = redis.Redis(host='localhost', port=6379, db=0)
def get_user(user_id):
cache_key = f'user:{user_id}'
cached = r.get(cache_key)
if cached:
return json.loads(cached)
user = fetch_user_from_db(user_id) # your DB call
r.setex(cache_key, 3600, json.dumps(user)) # cache for 1 hour
return user
모범 사례:
- 오래된 데이터를 피하기 위해 항상 만료 시간(TTL)을 설정하세요.
user:123:profile과 같이 의미 있는 키 이름을 사용하세요.- 캐시 무효화를 처리하세요: 데이터가 변경되면 캐시 항목을 업데이트하거나 삭제하세요.
- 메모리를 절약하기 위해 객체의 여러 필드를 저장할 때 Redis 해시 사용을 고려하세요.
고급 캐싱 패턴
트래픽이 많은 애플리케이션의 경우 다음 패턴을 고려하세요:
- Write-through: 캐시와 데이터베이스를 함께 업데이트합니다.
- Write-behind: 캐시를 먼저 업데이트한 다음 비동기적으로 데이터베이스를 업데이트합니다.
- Refresh-ahead: 만료되기 전에 캐시 항목을 사전에 새로 고칩니다.
Redis를 사용한 큐
Redis 리스트와 스트림은 간단한 메시지 큐를 구축하는 데 완벽합니다. 기본 작업 큐에는 리스트를, 더 복잡한 이벤트 처리에는 스트림을 사용하세요.
리스트를 사용한 간단한 작업 큐
LPUSH로 작업을 추가하고 BRPOP으로 처리합니다. BRPOP은 작업이 사용 가능할 때까지 블록되어 워커에 효율적입니다.
# Producer
r.lpush('jobs', json.dumps({'task': 'send_email', 'to': 'user@example.com'}))
# Consumer (worker)
while True:
_, job_data = r.brpop('jobs', timeout=30)
job = json.loads(job_data)
process_job(job)
모범 사례:
- 다른 작업 유형이나 우선순위에 대해 별도의 리스트를 사용하세요.
- 실패한 작업을 위한 데드 레터 큐를 구현하세요.
BRPOPLPUSH(또는 Redis 6.2+의BLMOVE)를 사용하여 작업을 처리 리스트로 원자적으로 이동시켜 신뢰성을 보장하세요.LLEN으로 큐 길이를 모니터링하여 워커를 확장하세요.
이벤트 소싱을 위한 Redis 스트림
Redis 스트림(Redis 5.0에서 도입)은 소비자 그룹, 승인, 재생 기능을 갖춘 더 강력한 큐를 제공합니다. XADD로 이벤트를 추가하고 XREADGROUP으로 소비하세요.
# Add event
r.xadd('events', {'type': 'order_placed', 'order_id': '123'})
# Consumer group
r.xgroup_create('events', 'order_processors', id='0', mkstream=True)
while True:
events = r.xreadgroup('order_processors', 'worker1', {'events': '>'}, count=10, block=5000)
for event in events:
process_event(event)
r.xack('events', 'order_processors', event.id)
Redis를 사용한 속도 제한
속도 제한은 API를 남용으로부터 보호하고 공정한 사용을 보장합니다. Redis의 원자적 연산은 요청을 계산하는 데 이상적입니다.
고정 윈도우 속도 제한
각 사용자에 대해 카운터를 증가시키고 만료를 설정합니다. 카운트가 제한을 초과하면 요청을 차단합니다.
def is_allowed(user_id, limit=100, window=60):
key = f'rate_limit:{user_id}'
current = r.incr(key)
if current == 1:
r.expire(key, window)
return current