Introdução ao Redis: chave-valor em memória

1. O que é Redis e por que usá-lo?

1.1. Definição: banco de dados chave-valor em memória

Redis (Remote Dictionary Server) é um banco de dados NoSQL do tipo chave-valor que armazena todos os dados primariamente na memória RAM. Diferente de bancos relacionais como PostgreSQL ou MySQL, que persistem dados em disco e usam memória apenas como cache temporário, o Redis trabalha com estruturas de dados na memória principal, oferecendo latências na ordem de microssegundos para operações de leitura e escrita.

1.2. Diferenças fundamentais entre Redis e bancos relacionais

Característica Bancos Relacionais (PostgreSQL) Redis
Armazenamento Disco (HD/SSD) Memória RAM
Modelo de dados Tabelas, linhas, colunas Chave-valor, hashes, listas
Linguagem de consulta SQL (estruturada) Comandos próprios (não-SQL)
ACID Transações completas Garantias mais simples
Velocidade Milissegundos Microssegundos
Persistência Garantida por padrão Opcional (RDB/AOF)

1.3. Casos de uso típicos

Redis é ideal para cenários que exigem alta performance e baixa latência:

  • Cache de consultas SQL: armazenar resultados de SELECT pesados
  • Gerenciamento de sessões: sessões de usuário em aplicações web
  • Filas de tarefas: processamento assíncrono com Lists
  • Contadores: likes, visualizações, métricas em tempo real
  • Rate limiting: controle de requisições por IP

2. Instalação e primeiros passos

2.1. Instalação local e via Docker

Instalação local (Ubuntu/Debian):

sudo apt update
sudo apt install redis-server
sudo systemctl start redis

Usando Docker (recomendado para testes rápidos):

docker run --name meu-redis -d -p 6379:6379 redis:7-alpine

2.2. Acessando o Redis com redis-cli

redis-cli
127.0.0.1:6379> PING
PONG

2.3. Comandos básicos: SET, GET, DEL, EXISTS

127.0.0.1:6379> SET usuario:1 "João Silva"
OK
127.0.0.1:6379> GET usuario:1
"João Silva"
127.0.0.1:6379> EXISTS usuario:1
(integer) 1
127.0.0.1:6379> DEL usuario:1
(integer) 1
127.0.0.1:6379> GET usuario:1
(nil)

3. Estruturas de dados fundamentais

3.1. Strings: armazenamento de valores simples e contadores

127.0.0.1:6379> SET contador:visitas 0
OK
127.0.0.1:6379> INCR contador:visitas
(integer) 1
127.0.0.1:6379> INCRBY contador:visitas 10
(integer) 11
127.0.0.1:6379> GET contador:visitas
"11"

3.2. Hashes: objetos com múltiplos campos

127.0.0.1:6379> HSET usuario:100 nome "Maria" idade 30 email "maria@email.com"
(integer) 3
127.0.0.1:6379> HGET usuario:100 nome
"Maria"
127.0.0.1:6379> HGETALL usuario:100
1) "nome"
2) "Maria"
3) "idade"
4) "30"
5) "email"
6) "maria@email.com"

3.3. Lists: filas e pilhas

127.0.0.1:6379> LPUSH tarefas "processar_pagamento"
(integer) 1
127.0.0.1:6379> RPUSH tarefas "enviar_email"
(integer) 2
127.0.0.1:6379> LLEN tarefas
(integer) 2
127.0.0.1:6379> LPOP tarefas
"processar_pagamento"
127.0.0.1:6379> RPOP tarefas
"enviar_email"

4. Trabalhando com TTL e expiração de chaves

4.1. Comandos EXPIRE, TTL e PERSIST

127.0.0.1:6379> SET cache:consulta "resultado_importante"
OK
127.0.0.1:6379> EXPIRE cache:consulta 60
(integer) 1
127.0.0.1:6379> TTL cache:consulta
(integer) 57
127.0.0.1:6379> PERSIST cache:consulta
(integer) 1
127.0.0.1:6379> TTL cache:consulta
(integer) -1

4.2. Estratégias de expiração

Redis utiliza três estratégias para remover chaves expiradas:

  • Passiva: quando uma chave expirada é acessada, Redis a remove
  • Ativa: a cada 100ms, Redis testa 20 chaves aleatórias e remove as expiradas
  • Lazy: durante operações de escrita, Redis verifica expiração

4.3. Exemplo prático: cache de consultas SQL com expiração automática

# Simulação: cache de resultado SELECT por 5 minutos
SET cache:select:produtos_mais_vendidos '{"dados": [{"id": 1, "nome": "Produto A"}]}'
EXPIRE cache:select:produtos_mais_vendidos 300

5. Redis como cache para bancos relacionais

5.1. Padrão cache-aside

O padrão cache-aside funciona assim:

  1. A aplicação verifica Redis primeiro
  2. Se encontrou (cache hit), retorna o valor
  3. Se não encontrou (cache miss), consulta o banco relacional
  4. Armazena o resultado no Redis com TTL
  5. Retorna o resultado

5.2. Estratégias de invalidação de cache

  • TTL (Time-To-Live): expiração automática após período definido
  • Notificações: invalidar cache quando o banco for atualizado
  • Write-through: atualizar cache simultaneamente ao banco

5.3. Exemplo: cache de resultados de SELECT com GET/SET

# Fluxo completo de cache-aside no redis-cli
# 1. Tentar obter do cache
GET cache:select:cliente_123

# 2. Se retornar nil, simular consulta ao banco e armazenar
SET cache:select:cliente_123 '{"id": 123, "nome": "Carlos", "saldo": 1500.50}'
EXPIRE cache:select:cliente_123 600

# 3. Próxima consulta retorna do cache (mais rápido)
GET cache:select:cliente_123

6. Persistência no Redis: RDB e AOF

6.1. Snapshotting (RDB)

RDB cria snapshots completos dos dados em disco em intervalos configurados:

# Configuração no redis.conf
save 900 1      # Salva se pelo menos 1 chave mudou em 15 minutos
save 300 10     # Salva se pelo menos 10 chaves mudaram em 5 minutos
save 60 10000   # Salva se pelo menos 10000 chaves mudaram em 1 minuto

6.2. Append-only file (AOF)

AOF registra cada operação de escrita em um log:

# Configuração no redis.conf
appendonly yes
appendfsync everysec  # Sincroniza a cada segundo (balanceado)

6.3. Comparação: quando usar RDB vs AOF vs ambos

Aspecto RDB AOF Ambos
Tamanho do arquivo Menor Maior Maior
Recuperação Mais rápida Mais lenta Flexível
Perda de dados Possível (intervalo) Mínima (segundos) Mínima
Uso recomendado Backups periódicos Alta durabilidade Produção crítica

7. Redis em produção: boas práticas e limitações

7.1. Limitações de memória RAM e políticas de evicção

Redis é limitado pela RAM disponível. Quando a memória atinge o limite, políticas de evicção definem quais chaves remover:

# Configuração no redis.conf
maxmemory 1gb
maxmemory-policy allkeys-lru  # Remove as chaves menos usadas recentemente

Políticas comuns:
- allkeys-lru: remove as menos usadas (recomendado para cache)
- volatile-ttl: remove as que expiram mais cedo
- noeviction: retorna erro quando a memória acaba

7.2. Monitoramento com INFO, MONITOR e SLOWLOG

# Estatísticas gerais
INFO memory
INFO stats
INFO keyspace

# Monitorar comandos em tempo real (cuidado em produção!)
MONITOR

# Ver comandos lentos (acima de 10ms por padrão)
SLOWLOG GET 10

7.3. Integração com aplicações

Python com redis-py:

import redis

r = redis.Redis(host='localhost', port=6379, decode_responses=True)
r.set('chave', 'valor')
print(r.get('chave'))

Node.js com ioredis:

const Redis = require('ioredis');
const redis = new Redis();

await redis.set('chave', 'valor');
console.log(await redis.get('chave'));

8. Conclusão e próximos passos

Redis oferece vantagens significativas sobre bancos relacionais tradicionais em cenários que exigem baixa latência e alta throughput. Sua natureza em memória permite operações em microssegundos, enquanto estruturas de dados como Strings, Hashes e Lists resolvem problemas comuns de cache, filas e contadores com simplicidade.

Para aprofundar, explore TTL e expiração avançada, estruturas como Sets e Sorted Sets, e padrões de cache mais sofisticados. Um exercício prático imediato: implemente um cache simples que armazena resultados de uma consulta SQL por 10 minutos, usando o padrão cache-aside.

Exercício prático

Implemente o seguinte fluxo:

# 1. Simule uma consulta SQL pesada
SET cache:select:vendas_2024 '{"total": 150000, "itens": 1200}'

# 2. Defina expiração de 5 minutos
EXPIRE cache:select:vendas_2024 300

# 3. Verifique o TTL
TTL cache:select:vendas_2024

# 4. Simule um cache miss (após expiração)
GET cache:select:vendas_2024

Redis não substitui bancos relacionais, mas os complementa perfeitamente, oferecendo uma camada de desempenho que transforma a experiência do usuário final.

Referências