Casos de Uso do Redis: Cache, Filas e Rate Limiting
Você adicionou o Redis à sua stack, mas está usando apenas como um simples armazenamento de chave-valor. O Redis oferece estruturas de dados e comandos poderosos que podem resolver cache, filas e rate limiting de forma elegante. Este guia mostra como implementar esses padrões de maneira eficaz.
Por que usar Redis para esses casos de uso?
O Redis é um armazenamento de dados em memória conhecido por sua velocidade e versatilidade. Ele suporta strings, hashes, listas, conjuntos, conjuntos ordenados e muito mais. Suas operações atômicas e expiração integrada o tornam ideal para cache, filas e rate limiting. Além disso, é fácil de integrar com qualquer linguagem.
Cache com Redis
Cache é o caso de uso mais comum do Redis. Você armazena dados acessados com frequência no Redis para reduzir a carga do banco de dados e acelerar as respostas.
Padrão básico de cache-aside
No padrão cache-aside, sua aplicação verifica o Redis primeiro. Se os dados não estiverem lá (cache miss), ela busca no banco de dados e os armazena no Redis para a próxima vez.
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) # sua chamada ao BD
r.setex(cache_key, 3600, json.dumps(user)) # cache por 1 hora
return user
Melhores práticas:
- Sempre defina um tempo de expiração (TTL) para evitar dados obsoletos.
- Use nomes de chaves significativos, como
user:123:profile. - Lide com invalidação de cache: quando os dados mudarem, atualize ou exclua a entrada do cache.
- Considere usar hashes do Redis para armazenar múltiplos campos de um objeto e economizar memória.
Padrões avançados de cache
Para aplicações de alto tráfego, considere estes padrões:
- Write-through: Atualize o cache e o banco de dados juntos.
- Write-behind: Atualize o cache primeiro, depois atualize o banco de dados de forma assíncrona.
- Refresh-ahead: Atualize proativamente as entradas do cache antes que expirem.
Filas com Redis
Listas e streams do Redis são perfeitos para construir filas de mensagens simples. Use listas para filas de tarefas básicas e streams para processamento de eventos mais complexos.
Fila de tarefas simples com listas
Use LPUSH para adicionar tarefas e BRPOP para processá-las. O BRPOP bloqueia até que uma tarefa esteja disponível, tornando-o eficiente para workers.
# Produtor
r.lpush('jobs', json.dumps({'task': 'send_email', 'to': 'user@example.com'}))
# Consumidor (worker)
while True:
_, job_data = r.brpop('jobs', timeout=30)
job = json.loads(job_data)
process_job(job)
Melhores práticas:
- Use listas separadas para diferentes tipos de tarefas ou prioridades.
- Implemente uma dead-letter queue para tarefas que falharam.
- Use
BRPOPLPUSH(ouBLMOVEno Redis 6.2+) para mover tarefas atomicamente para uma lista de processamento, garantindo confiabilidade. - Monitore o comprimento da fila com
LLENpara escalar workers.
Redis Streams para Event Sourcing
Redis Streams (introduzido no Redis 5.0) fornece uma fila mais robusta com grupos de consumidores, confirmações e replay. Use XADD para adicionar eventos e XREADGROUP para consumi-los.
# Adicionar evento
r.xadd('events', {'type': 'order_placed', 'order_id': '123'})
# Grupo de consumidores
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)
Rate Limiting com Redis
Rate limiting protege suas APIs contra abusos e garante uso justo. As operações atômicas do Redis o tornam ideal para contar requisições.
Rate Limiting de Janela Fixa
Incremente um contador para cada usuário e defina uma expiração. Se a contagem exceder o limite, bloqueie a requisição.
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