Casos de Uso do Redis: Cache, Filas e Rate Limiting
Você construiu uma aplicação web que funciona bem em desenvolvimento, mas conforme o tráfego cresce, você percebe tempos de resposta lentos e sobrecarga do banco de dados. O culpado geralmente são operações caras repetidas: buscar os mesmos dados do banco de dados, processar tarefas de forma síncrona ou permitir taxas de requisição abusivas. O Redis, um armazenamento de dados em memória, oferece soluções elegantes para esses problemas. Neste artigo, exploraremos três casos de uso práticos do Redis: cache, filas e rate limiting. Você aprenderá como implementar cada padrão com exemplos de código e entenderá quando aplicá-los.
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 e hashes, tornando-o adequado para várias tarefas além de simples cache. Como mantém os dados em memória, o Redis oferece latência inferior a um milissegundo, o que é ideal para cenários de alto desempenho. Ele também oferece opções de persistência, para que você não perca dados ao reiniciar. Vamos mergulhar nos três casos de uso.
1. Cache com Redis
O cache é o caso de uso mais comum do Redis. A ideia é simples: armazenar os resultados de operações caras (como consultas ao banco de dados ou chamadas de API) no Redis e servi-los diretamente para requisições subsequentes. Isso reduz a carga no seu banco de dados principal e acelera os tempos de resposta.
Como Implementar o Cache
- Verifique o cache: Antes de acessar o banco de dados, consulte o Redis pelos dados usando uma chave única (por exemplo,
user:123). - Se encontrado (cache hit): Retorne os dados em cache imediatamente.
- Se não encontrado (cache miss): Busque os dados do banco de dados, armazene-os no Redis com um tempo de expiração (TTL) e retorne-os.
Aqui está um exemplo 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 de dados
user = fetch_user_from_db(user_id)
r.setex(cache_key, 3600, json.dumps(user)) # Cache por 1 hora
return user
Melhores práticas: Escolha chaves significativas, defina TTLs apropriados para evitar dados obsoletos e considere estratégias de invalidação de cache. Para cargas de trabalho com muitas leituras, o cache pode reduzir a carga do banco de dados em ordens de magnitude.
2. Filas com Redis
As filas desacoplam tarefas demoradas do ciclo de requisição-resposta. Por exemplo, enviar e-mails, processar imagens ou gerar relatórios podem ser feitos de forma assíncrona. Listas ou streams do Redis são perfeitos para implementar filas simples.
Implementando uma Fila Básica
Use LPUSH para adicionar trabalhos à esquerda de uma lista e BRPOP para bloquear e remover da direita. Isso cria uma fila FIFO.
# Produtor: adiciona trabalho
r.lpush('email_queue', json.dumps({'to': 'user@example.com', 'subject': 'Welcome'}))
# Consumidor: processa trabalhos
while True:
_, job = r.brpop('email_queue', timeout=30)
if job:
process_email(json.loads(job))
Para necessidades mais avançadas, os Redis Streams fornecem grupos de consumidores, confirmações e reprodutibilidade, tornando-os adequados para processamento robusto de mensagens.
3. Rate Limiting com Redis
O rate limiting protege sua API contra abusos e garante uso justo. As operações atômicas do Redis facilitam a implementação de contadores que expiram automaticamente.
Rate Limiting com Janela Fixa
Na abordagem de janela fixa, você permite N requisições por janela de tempo (por exemplo, 100 requisições por hora por IP). Use INCR e EXPIRE:
def is_allowed(ip, limit=100, window=3600):
key = f'rate:{ip}'
current = r.incr(key)
if current == 1:
r.expire(key, window)
return current