Primeiro decida se precisa de cache

Antes de adicionar Redis, meça a consulta, a carga e o objetivo de latência. Cache não corrige automaticamente uma consulta mal indexada, aumenta o número de componentes a operar e pode devolver informação antiga. Também esclareça se Redis será apenas acelerador ou fonte de dados: a estratégia de persistência e recuperação muda conforme o papel.

Cache-aside: o padrão mais simples

Na estratégia cache-aside, a aplicação tenta obter o valor do cache. Se não encontrar, lê a fonte de verdade, armazena uma cópia com expiração e responde. Uma atualização grava primeiro na fonte de verdade e invalida a chave associada; TTL limita por quanto tempo uma chave esquecida pode permanecer.

public async Task<Produto?> ObterAsync(
    string id, CancellationToken cancellationToken)
{
    var key = $"produto:{id}";
    var json = await _cache.StringGetAsync(key);
    if (json.HasValue)
        return JsonSerializer.Deserialize<Produto>(json!);

    var produto = await _repositorio.ObterAsync(id, cancellationToken);
    if (produto is not null)
    {
        var valor = JsonSerializer.Serialize(produto);
        await _cache.StringSetAsync(key, valor, TimeSpan.FromMinutes(5));
    }
    return produto;
}

O exemplo é conceitual: trate falhas de serialização, chaves e versões do formato, cancelamento e políticas de falha do cache conforme os requisitos da aplicação. Não mantenha conexão Redis por requisição; use o ciclo de vida recomendado pelo cliente .NET.

Invalidação e consistência

Na atualização, gravar no banco e remover a chave Redis são duas operações separadas. Uma falha entre elas pode deixar uma cópia antiga até o TTL expirar. Para dados sensíveis à consistência, defina explicitamente a tolerância a stale data, invalide após persistir a mudança e considere versão de chave ou outra estratégia quando necessário.

TTL não é apenas limpeza: é parte do contrato de consistência. Tempos menores aumentam misses e carga no banco; tempos maiores prolongam a janela de dados antigos.

Cache stampede, avalanche e chaves quentes

Se uma chave popular expira, muitas requisições simultâneas podem consultar o banco ao mesmo tempo — um cache stampede. Um lock de curta duração, coalescência de requisições, refresh antecipado ou jitter nos TTLs podem ajudar, cada qual com custos e semântica próprios. Evite uma expiração sincronizada de muitas chaves e monitore chaves que concentram tráfego.

Locks distribuídos não são um recurso gratuito nem substituem transações. Use-os apenas com análise de lease, expiração e recuperação após falha, especialmente quando protegem efeitos que não podem se repetir.

O que acontece se Redis ficar indisponível?

Escolha uma política de cache degradado: ignorar cache temporariamente e consultar a fonte, ou falhar a operação quando a fonte não suportar a carga. A escolha depende do produto. Sem limites de timeout e concorrência, um Redis lento pode consumir recursos da aplicação; timeouts, bulkheads e fallback seguro conectam o desenho às práticas de resiliência.

Meça cache hit/miss, latência, erros, memória, evictions, conexões e latência da fonte de dados. Traces ajudam a enxergar se o tempo foi gasto no cache ou no banco: veja observabilidade.

Redis não substitui automaticamente o banco

Cache pode perder chaves, expulsar dados conforme política de memória ou ficar indisponível. Se a informação é fonte de verdade, modele persistência, backups, replicação e recuperação para esse requisito, e não presuma que configuração padrão atende. Também proteja o serviço com autenticação, rede privada, TLS e políticas de acesso adequadas ao ambiente.

Leituras relacionadas