Casos de Uso do Redis: Cache, Filas e Rate Limiting
Você construiu uma aplicação web que funciona, mas conforme o tráfego cresce, você percebe carregamentos lentos, gargalos no banco de dados e eventuais indisponibilidades. Esses são problemas comuns de escalabilidade. O Redis, um armazenamento de dados em memória, pode resolver muitos desses problemas de forma elegante. Neste artigo, vamos explorar três casos de uso práticos do Redis: caching, filas e rate limiting. Você aprenderá como implementar cada um com exemplos de código e boas práticas.
Por que Redis?
O Redis é um armazenamento de chave-valor em memória, de código aberto, conhecido por sua velocidade e versatilidade. Ele suporta estruturas de dados como strings, listas, conjuntos, hashes e conjuntos ordenados, tornando-o adequado para uma ampla variedade de tarefas. Como mantém os dados em memória, leituras e escritas são extremamente rápidas — frequentemente abaixo de um milissegundo. Embora não substitua um banco de dados principal, ele se destaca como uma ferramenta complementar para desempenho e coordenação.
1. Cache com Redis
O caching é o caso de uso mais comum do Redis. Ao armazenar dados acessados com frequência no Redis, você reduz a carga no banco de dados principal e acelera as respostas.
Como implementar o cache
- Verifique o Redis primeiro: Quando uma requisição chega, procure os dados no Redis usando uma chave única.
- Em caso de cache miss: Se os dados não estiverem no Redis, busque-os no banco de dados.
- Armazene no Redis: Salve o resultado no Redis com um tempo de expiração (TTL) para evitar dados obsoletos.
- Retorne os dados: Envie os dados para o cliente.
Aqui está um exemplo simples em Python usando redis-py:
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)
# Simula busca no banco
user = fetch_user_from_db(user_id)
r.setex(cache_key, 3600, json.dumps(user)) # cache por 1 hora
return user
Boas práticas:
- Use nomes de chave significativos com dois-pontos, por exemplo,
user:123. - Defina um TTL para evitar dados obsoletos e consumo excessivo de memória.
- Considere estratégias de invalidação de cache: write-through, write-behind ou expiração baseada em tempo.
- Para objetos complexos, serialize com JSON ou MessagePack.
2. Filas com Redis
Listas e streams do Redis são perfeitos para construir filas de mensagens simples. Eles permitem desacoplar tarefas demoradas (como enviar e-mails ou processar imagens) do fluxo principal de requisições, melhorando a capacidade de resposta.
Usando listas como filas
Listas do Redis suportam operações atômicas de push e pop. Use LPUSH para adicionar tarefas à esquerda e BRPOP para bloquear e retirar da direita, criando uma fila FIFO.
# Produtor: adiciona uma tarefa
r.lpush('job_queue', json.dumps({'task': 'send_email', 'to': 'user@example.com'}))
# Consumidor: processa tarefas
while True:
_, job_data = r.brpop('job_queue', timeout=30)
job = json.loads(job_data)
process_job(job)
Considerações:
- Confiabilidade: Listas não são persistentes por padrão; habilite a persistência AOF ou RDB se necessário.
- Entrega pelo menos uma vez: Use
BRPOPLPUSHpara mover tarefas para uma lista de processamento e remova após a conclusão. - Streams: Para casos de uso mais avançados (grupos de consumidores, confirmações), considere o Redis Streams.
3. Rate Limiting com Redis
O rate limiting protege sua API contra abusos e garante uso justo. As operações atômicas e a expiração do Redis o tornam ideal para implementar limitadores de taxa.
Rate limiting de janela fixa
Em uma janela fixa, você permite N requisições por intervalo de tempo (por exemplo, 100 requisições por hora). Use INCR e EXPIRE para rastrear contagens.
def is_rate_limited(user_id, limit=100, window=3600):
key = f"rate:{user_id}"
current = r.incr(key)
if current == 1:
r.expire(key, window)
return current > limit
Janela deslizante com conjuntos ordenados proporciona um limite mais suave. Armazene os timestamps das requisições em um conjunto ordenado e remova as entradas antigas.
def is_rate_limited_sliding(user_id, limit=100, window=3600):
key = f"rate:{user_id}"
now = time.time()
pipeline = r.pipeline()
pipeline.zremrangebyscore(key, 0, now - window)
pipeline.zadd(key, {now: now})
pipeline.zcard(key)
pipeline.expire(key, window)
_, _, count, _ = pipeline.execute()
return count > limit
Boas práticas:
- Use uma chave única por usuário ou IP.
- Combine com middleware para uma integração limpa.
- Considere usar módulos do Redis como
redis-cellpara algoritmos avançados.
Comparação dos Casos de Uso
| Caso de Uso | Estrutura de Dados Redis | Benefício Principal |
|---|---|---|
| Cache | String, Hash | Reduz carga no banco, leituras mais rápidas |
| Filas | List, Stream | Desacopla tarefas, melhora capacidade de resposta |
| Rate Limiting | String, Sorted Set | Protege APIs, garante uso justo |
FAQ
O Redis é persistente?
O Redis oferece persistência por meio de snapshots RDB e logs AOF, mas é principalmente um armazenamento em memória. Para dados críticos, sempre tenha um backup ou use um banco de dados principal.
Posso usar o Redis para armazenamento de sessão?
Sim, o Redis é comumente usado para armazenamento de sessão por sua velocidade e expiração automática. Ele permite compartilhar sessões entre vários servidores.
O que acontece se o Redis ficar indisponível?
Se o Redis estiver indisponível, sua aplicação deve recorrer graciosamente ao banco de dados ou retornar dados em cache de um cache local. Implemente circuit breakers para evitar falhas em cascata.
Pronto para otimizar sua aplicação web? Comece integrando o Redis para cache, filas ou rate limiting. Para tarefas rápidas como formatar JSON ou analisar logs, confira nosso JSON Formatter e Analisador de Log do Nginx.