Capítulo 15: Cache Avançado com Redis e Tags de Cache
Especialização em Backend com PHP & Laravel • Laravel 11 & PHP 8.3+ • Eloquent ORM, Sanctum, Filas, Horizon e APIs Corporativas
🗺️ Mapa Conceitual do Tópico
flowchart TD
A["Cliente HTTP / Frontend"] --> B["API Gateway / Router"]
B --> C["Controller / Handler"]
C --> D["Service Layer (Regras de Negócio)"]
D --> E["Repository / ORM (Persistência)"]
E --> F["Banco de Dados / Cache"]
subgraph ARQ["Arquitetura do Capítulo"]
G["Conceito: Cache Avançado com Redis e Tags de Cache"]
H["Segurança, Validação e Resiliência"]
I["Alta Performance e Escalabilidade"]
end
D --> ARQ
style A fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
style B fill:#fff3e0,stroke:#ff9800,stroke-width:2px
style C fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
style D fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
style E fill:#fce4ec,stroke:#e91e63,stroke-width:2px
style F fill:#f3e5f5,stroke:#9c27b0,stroke-width:2px
🏛️ 1. Cache::remember(): Auto-População Atômica
O padrão mais comum de cache é “ler do cache; se não existir, calcular e guardar” — escrito manualmente isso são 4-5 linhas repetidas em todo lugar. Cache::remember() faz isso numa chamada só, e é atômico o suficiente para evitar a maioria das condições de corrida:
<?php
use Illuminate\Support\Facades\Cache;
use App\Models\Produto;
$produtos = Cache::remember('produtos:ativos', 3600, function () {
return Produto::where('ativo', true)->get();
});
Se a chave produtos:ativos existe no Redis, o closure nunca executa — a query ao banco é pulada inteiramente. Isso é a diferença estrutural entre cache e apenas “guardar um valor”: o objetivo é evitar o trabalho caro (a query), não só evitar retransmitir o resultado.
🏷️ 2. O Problema que Cache Tags Resolve: Invalidação Seletiva
Sem tags, a única forma de invalidar cache relacionado a um produto é ou (a) dar Cache::forget() numa chave específica que você precisa saber de cor, ou (b) Cache::flush() — que apaga absolutamente tudo, inclusive cache de outras partes do sistema sem relação nenhuma. Cache Tags resolvem isso agrupando entradas por rótulo lógico, permitindo invalidar só o grupo relevante:
<?php
// Gravando com múltiplas tags
Cache::tags(['produtos', 'categorias'])->put('produtos:categoria:5', $produtos, 3600);
// Invalidando SÓ o que está marcado como 'produtos' -- 'categorias' continua intacto
Cache::tags('produtos')->flush();
Por baixo dos panos, o driver Redis mantém um índice reverso de “quais chaves pertencem a qual tag”, então o flush() de uma tag não precisa varrer o banco inteiro procurando chaves — ele consulta esse índice e apaga só as chaves marcadas.
⚠️ Gotcha importante: Cache Tags só funcionam com drivers que suportam essa indexação (Redis e Memcached) — o driver file ou database do Laravel lança exceção se você tentar usar Cache::tags() com eles.
🔒 3. Cache-Aside vs. Write-Through
O padrão usado em Cache::remember() é chamado Cache-Aside (ou lazy loading): a aplicação só popula o cache quando um read acontece e a chave está faltando. A alternativa é Write-Through: popular o cache no momento exato da escrita (update/create), garantindo que o cache nunca fique desatualizado às custas de escrever em dois lugares toda vez:
public function atualizar(Produto $produto, array $dados): Produto
{
$produto->update($dados);
// Write-Through: atualiza o cache no mesmo instante da escrita no banco
Cache::tags('produtos')->put("produto:{$produto->id}", $produto, 3600);
return $produto;
}
Cache-Aside é mais simples e cobre a maioria dos casos; Write-Through evita a janela (ainda que pequena) entre a escrita no banco e a próxima leitura repopular o cache, ao custo de mais pontos de código que precisam lembrar de manter o cache em sincronia.