Performance e otimização
Gerenciamento eficiente de memória
Estratégias de otimização de memória no Redis — encoding interno, maxmemory, políticas de eviction, fragmentação e monitoramento.
Nesta aula você vai
- Compreender encodings internos do Redis e seu impacto na memória
- Configurar maxmemory e políticas de eviction adequadas ao domínio
- Detectar e mitigar fragmentação de memória em produção
Gerenciamento eficiente de memória
Objetivos
Nesta aula você vai:
- Entender como o Redis armazena dados internamente (encodings)
- Configurar limites de memória e políticas de eviction
- Detectar fragmentação e aplicar estratégias de mitigação
Conteúdo
A memória como narrativa finita
Todo romance tem limite de páginas; toda instância Redis tem limite de RAM. Diferente de um disco que cresce com demanda, a memória de um servidor é finita e compartilhada. Quando o Redis atinge maxmemory, algo precisa ser sacrificado — e a escolha do que evictar define se o sistema continua saudável ou entra em espiral de cache miss.
Gerenciar memória no Redis é planejar o final da história antes que ela se escreva sozinha.
Anatomia do uso de memória
# Visão geral
redis-cli INFO memory
# Campos essenciais:
# used_memory: bytes alocados pelo Redis
# used_memory_rss: bytes que o OS vê (RSS)
# used_memory_peak: pico histórico
# maxmemory: limite configurado
# mem_fragmentation_ratio: RSS / used_memory
| Métrica | Significado saudável | Alerta |
|---|---|---|
used_memory / maxmemory |
< 80% | > 90% — eviction iminente |
mem_fragmentation_ratio |
1,0 – 1,5 | > 1,5 — fragmentação; < 1,0 — swap! |
used_memory_rss - used_memory |
Poucos MB | Diferença grande → fragmentação |
Encodings internos: como o Redis economiza
O Redis escolhe automaticamente a representação mais compacta:
| Tipo | Encoding | Condição | Economia |
|---|---|---|---|
| String | int |
Valor numérico inteiro | Sem overhead de string |
| String | embstr |
String ≤ 44 bytes | Alocação única |
| Hash | listpack |
≤ 128 campos, valores pequenos | ~50% vs hash table |
| List | listpack |
Elementos pequenos | Compacto |
| Set | intset |
Apenas inteiros, ≤ 512 membros | Muito compacto |
| Sorted Set | listpack |
Elementos pequenos | Compacto |
# Verificar encoding de uma chave
redis-cli OBJECT ENCODING usuario:42
# "listpack" — compacto
redis-cli OBJECT ENCODING usuario:999
# "hashtable" — campos demais ou valores grandes
# Forçar conversão (quando chave cresce além do limite do encoding)
redis-cli HSET usuario:42 campo_extra "valor muito longo que excede o limite do listpack..."
redis-cli OBJECT ENCODING usuario:42
# "hashtable" — agora usa mais memória
Dica de design: mantenha hashes com poucos campos e valores curtos para aproveitar listpack.
Configurando maxmemory
# Definir limite (ex.: 2 GB)
redis-cli CONFIG SET maxmemory 2gb
# Verificar
redis-cli CONFIG GET maxmemory
Regra: defina maxmemory para 70–80% da RAM disponível no servidor, reservando espaço para OS, replicação buffer e AOF rewrite.
Servidor com 8 GB RAM:
OS + buffers: 1 GB
Redis maxmemory: 5 GB (reserva 2 GB)
Pico AOF rewrite: usa reserva
Políticas de eviction
Quando maxmemory é atingido, o Redis remove chaves conforme a política:
| Política | Comportamento | Caso de uso |
|---|---|---|
noeviction |
Retorna erro em escritas | Dados críticos — nunca perder |
allkeys-lru |
Remove chave menos usada (qualquer) | Cache puro |
allkeys-lfu |
Remove chave menos frequentemente usada | Cache com padrão de acesso estável |
volatile-lru |
Remove chave com TTL menos usada | Cache com TTL em todas as chaves |
volatile-lfu |
Remove chave com TTL menos frequente | Cache com TTL + padrão estável |
volatile-ttl |
Remove chave com TTL mais próximo de expirar | Prioriza retenção de chaves "frescas" |
allkeys-random |
Remove chave aleatória | Raro — testes |
# Configurar política
redis-cli CONFIG SET maxmemory-policy allkeys-lru
# Verificar estatísticas de eviction
redis-cli INFO stats | grep evicted
# evicted_keys:12345
LFU vs LRU: LFU (Least Frequently Used) é superior quando o padrão de acesso é estável ao longo do tempo — evita que um scan único remova dados frequentemente acessados. LRU é melhor quando o padrão muda frequentemente.
TTL: o eviction programado
A melhor eviction é a que você programa:
# Sempre defina TTL em dados de cache
SET cache:produto:1001 "dados" EX 300
# Verificar TTL restante
TTL cache:produto:1001
# Renovar TTL em acesso (sliding expiration)
EXPIRE cache:produto:1001 300
# Definir TTL em hash existente
EXPIRE usuario:42 3600
| Estratégia | TTL | Eviction policy |
|---|---|---|
| Cache de catálogo | 5–30 min | volatile-lru |
| Sessão de usuário | 24h | volatile-lru |
| Rate limiting | 60s | volatile-ttl |
| Dados críticos (lock) | Curto + noeviction |
Nunca evictar |
Fragmentação de memória
Quando mem_fragmentation_ratio > 1,5, o Redis usa mais RAM do que os dados efetivamente ocupam:
# Diagnóstico
redis-cli INFO memory | grep fragmentation
# mem_fragmentation_ratio:1.82
# Causas comuns:
# - Updates frequentes em chaves de tamanho variável
# - Deletes massivos seguidos de novas alocações
# - AOF rewrite e BGSAVE (fork COW)
Mitigações:
# 1. Redis 4.0+: ativar defrag ativo
redis-cli CONFIG SET activedefrag yes
redis-cli CONFIG SET active-defrag-threshold-lower 10
redis-cli CONFIG SET active-defrag-threshold-upper 25
# 2. Restart controlado (último recurso)
# Salvar, reiniciar, carregar — memória contígua
# 3. Redis 7.0+: jemalloc tuning
redis-cli CONFIG SET maxmemory-eviction-tenacity 10
Monitoramento proativo
# MEMORY STATS (Redis 4.0+) — breakdown detalhado
redis-cli MEMORY STATS
# Uso de memória por chave
redis-cli MEMORY USAGE usuario:42
# Maiores chaves (amostragem — seguro em produção)
redis-cli --bigkeys
# Análise completa (pode demorar)
redis-cli --memkeys --memkeys-samples 5
Exemplo de saída --bigkeys:
-------- biggest string keys --------
cache:produto:1001 (serialized JSON) - 524288 bytes
-------- biggest hash keys --------
usuario:48291 (47 fields) - 8192 bytes
-------- summary --------
Total keyspace: 2500000 keys
Peak memory: 4.2 GB
Estratégias de redução de memória
| Técnica | Economia estimada | Trade-off |
|---|---|---|
| Hash em vez de JSON string | 30–50% | Menos flexibilidade |
| Comprimir valores grandes (na app) | 50–80% | CPU de compressão |
| Reduzir tamanho de chaves | 5–15% | Legibilidade |
| TTL agressivo | Variável | Mais cache miss |
| Particionar em múltiplas instâncias | N/A | Complexidade operacional |
# Chaves curtas economizam memória em escala
# Ruim: cache:product:category:electronics:item:1001
# Bom: c:p:el:1001
# Comparação de overhead
redis-cli MEMORY USAGE "cache:product:category:electronics:item:1001"
redis-cli MEMORY USAGE "c:p:el:1001"
Alertas recomendados (Prometheus)
# Memória acima de 85% do maxmemory
redis_memory_used_bytes / redis_memory_max_bytes > 0.85
# Fragmentação alta
redis_mem_fragmentation_ratio > 1.5
# Eviction ativa (chaves sendo removidas)
rate(redis_evicted_keys_total[5m]) > 0
Resumo
- Redis usa encodings compactos (listpack, intset) — design de chaves influencia memória
- Configure
maxmemory(70–80% da RAM) e política de eviction alinhada ao domínio - Monitore
mem_fragmentation_ratioe ativeactivedefragquando > 1,5 - TTL em todas as chaves de cache é a melhor política de eviction
Para refletir
Execute redis-cli --bigkeys no seu ambiente. Qual tipo de chave consome mais memória? A estrutura de dados é a ideal para o padrão de acesso?