A invalidação de cache em PHP não consiste em escolher um TTL e armazenar respostas no Redis. É uma decisão de consistência: determinar quais informações podem sofrer atraso, por quanto tempo e o que deve acontecer quando o dado original muda. Um cache mal projetado pode mostrar um preço antigo, conceder acesso com permissões revogadas ou apresentar uma disponibilidade que já não existe. Um cache conservador demais, por outro lado, transfere toda a carga para o banco de dados e perde seu propósito.
O ponto de partida é tratar cada entrada como um dado com proprietário, ciclo de vida e risco definidos. Isso permite que produto, negócio e tecnologia concordem sobre quando uma leitura potencialmente obsoleta é aceitável e quando o estado atual da fonte da verdade deve ser obtido.
Classifique os dados antes de colocá-los em cache

Nem todos os dados frequentes devem ser colocados em cache, nem todos aceitam o mesmo mecanismo. Avalie cada leitura com quatro critérios: volatilidade, impacto da desatualização, custo de consultar a origem e tolerância a uma falha de cache.
- Baixa volatilidade e baixo impacto: catálogos públicos, metadados ou configurações não sensíveis normalmente aceitam TTL de minutos ou horas, conforme seu processo de mudança.
- Volatilidade média: páginas de produto, agregados de painéis e resultados de busca podem ser colocados em cache se forem invalidados quando os registros que os compõem mudarem.
- Alto impacto: permissões, saldos, limites, estados transacionais, estoque durante a confirmação de compra e controles de autorização exigem uma fonte da verdade consistente ou uma estratégia explícita de frescor muito rigorosa.
- Dados caros de calcular: relatórios e resumos derivados podem justificar cache mesmo que não sejam muito consultados, mas devem declarar quais entidades os invalidam.
É útil separar o cache de apresentação do cache de decisão. Exibir por alguns segundos o nome anterior de uma categoria pode ser aceitável. Usar uma política antiga para autorizar uma operação normalmente não é. Para decisões críticas, consulte a origem autoritativa ou armazene versões que possam ser verificadas antes de agir.
Defina propriedade, chaves e contratos de frescor
Cada entrada precisa de uma ficha operacional. Ela deve indicar a fonte da verdade, a chave, os consumidores, o TTL máximo, o evento de invalidação, o comportamento caso o Redis não esteja disponível e o responsável funcional ou técnico. Sem esse contrato, as chaves se multiplicam e ninguém sabe o que apagar após uma mudança.
Use chaves previsíveis e com escopo suficiente. Por exemplo, product:42 representa uma entidade específica; tenant:8:product:42 evita misturar dados entre organizações; e dashboard:tenant:8:period:current identifica um resultado derivado. Não inclua dados secretos nas chaves nem use serializações instáveis como identidade.
Também é conveniente manter um formato de valor uniforme: payload, versão ou data de geração e, quando relevante, um indicador de frescor. Um consumidor não deve presumir que uma resposta em cache é equivalente a uma leitura transacionalmente consistente.
final class ProductCacheKey
{
public static function detail(int $tenantId, int $productId): string
{
return "tenant:{$tenantId}:product:{$productId}:v1";
}
}O sufixo de esquema permite mudar a estrutura do valor sem precisar localizar e remover todas as entradas históricas. Ele não substitui a invalidação do dado de negócio, mas reduz riscos durante uma evolução de formato.
Escolha o padrão de atualização conforme o tipo de leitura
Cache-aside para leituras reutilizáveis
Com cache-aside, a aplicação busca primeiro a chave; em caso de ausência, consulta o banco de dados, constrói o valor e o armazena com TTL. É simples e adequado para leituras relativamente estáveis. Seu limite é claro: após uma escrita, alguém deve remover ou substituir as entradas afetadas.
A invalidação deve ocorrer depois de confirmar a transação. Apagar antes do commit pode fazer com que outro processo reconstrua o cache com o valor ainda antigo. Se a aplicação publica eventos, um padrão outbox ajuda a registrar a mudança na mesma transação e a entregar posteriormente a ordem de invalidação de forma confiável.
Atualização explícita e versionamento
Se uma entidade é lida com muita frequência e suas mudanças são controladas, a entrada pode ser atualizada após a confirmação da escrita. Assim, evita-se o próximo cache miss. No entanto, o processo deve gerar exatamente a mesma representação esperada pelos leitores; caso contrário, invalidar e reconstruir geralmente é menos arriscado.
O versionamento de chaves é útil para dependências amplas. Em vez de apagar todas as listas de produtos de uma organização, incrementa-se tenant:8:products:version e as listas incorporam esse número em sua chave. As listas antigas expiram sozinhas. Essa abordagem reduz remoções em massa, mas exige controlar o crescimento das chaves e não deve ser usada para ocultar uma dependência mal compreendida.
Controle condições de corrida e dependências derivadas
A condição de corrida típica ocorre assim: uma leitura falha no cache e consulta o valor antigo; uma escrita é confirmada e invalida; a primeira leitura termina e volta a armazenar o valor antigo. Para dados sensíveis, combine a invalidação com versão de entidade ou bloqueio breve de reconstrução. Antes de gravar o valor calculado, verifique se a versão consultada continua sendo a atual. Caso não seja, descarte o resultado e leia novamente.
Os bloqueios distribuídos devem ser curtos, ter expiração e não se tornar um único ponto de bloqueio. Sua função é reduzir reconstruções simultâneas, não garantir sozinhos a consistência de negócio. Se o bloqueio não for adquirido, uma opção é aguardar brevemente pelo valor reconstruído ou permitir uma leitura direta limitada.
A invalidação por dependência exige inventário. Uma mudança de produto pode afetar seu detalhe, várias listas, resultados de busca, contadores e um painel. Uma mudança de função pode afetar permissões efetivas de usuários e menus derivados. Modele essas relações de forma explícita:
- Invalide a entidade direta por meio de sua chave.
- Invalide ou versione as coleções e os agregados que dependem dela.
- Recalcule de forma assíncrona os resultados caros se a experiência permitir.
- Não confunda limpar uma visualização com atualizar a fonte da verdade.
Quando a relação não é facilmente enumerável, um espaço de versões por organização, catálogo ou política normalmente é mais seguro do que tentar descobrir todas as chaves afetadas por meio de padrões de remoção global.
Use TTL, jitter e limites para proteger a origem
O TTL é uma rede de segurança, não o único mecanismo de coerência. Mesmo uma chave corretamente invalidada deve expirar: podem existir falhas na entrega de eventos, erros de deploy ou entradas órfãs. Escolha o TTL conforme o custo do erro, não apenas conforme o custo da consulta.
Aplique jitter aleatório ao TTL para que milhares de chaves criadas ao mesmo tempo não expirem simultaneamente. Além disso, proteja a origem contra uma avalanche de cache misses com bloqueio de reconstrução por chave, limites de concorrência e cotas por consumidor. Para dados não críticos, pode-se servir um valor ligeiramente expirado enquanto um único processo o recalcula; para permissões ou disponibilidade para decisões, essa técnica deve ser descartada ou limitada a cenários expressamente aprovados.
Projete a degradação quando o Redis falhar
O Redis é uma dependência operacional, não a fonte da verdade. Se ele não responder, a aplicação precisa de um modo de degradação definido. Para uma leitura pública pouco custosa, pode consultar diretamente o banco de dados com limites de tempo. Para consultas caras, convém aplicar limitação de carga, reduzir campos, responder com um estado temporariamente indisponível ou usar uma réplica apropriada se a arquitetura a contemplar.
Não transforme uma falha de cache em esgotamento de conexões do banco de dados. Defina timeouts curtos, circuit breakers, orçamentos de consultas e métricas por rota. Em dados críticos, é preferível rejeitar uma operação a tomar uma decisão com permissões, saldos ou estoque cujo frescor não pode ser garantido.
Teste e observe o frescor, não apenas os acertos
Uma alta taxa de acerto não demonstra que o cache esteja correto. Instrumente acertos, falhas, latência, erros de leitura e escrita, TTL restante, bloqueios de reconstrução, invalidações emitidas e com falha, além de consultas e saturação do banco de dados. Relacione esses sinais a cada família de chaves e não apenas ao Redis como serviço global.
Em testes, cubra ao menos a leitura inicial, a atualização posterior, a remoção, a invalidação após o commit, a queda do cache e as condições de corrida entre leitor e gravador. Verifique que um usuário perde o acesso após a revogação de uma permissão, que uma lista reflete uma modificação conforme seu contrato de frescor e que uma invalidação com falha ativa alertas ou recuperação.
Lista de verificação para uma aplicação PHP existente

- Liste as leituras repetidas e classifique-as por risco, volatilidade e custo.
- Declare a fonte da verdade e a tolerância máxima à obsolescência de cada dado.
- Documente chaves, TTL, dependências, consumidores e evento de invalidação.
- Execute invalidações ou atualizações somente após o commit confirmado.
- Proteja reconstruções simultâneas e adicione jitter a expirações relevantes.
- Defina o modo degradado diante da indisponibilidade do Redis sem sobrecarregar o banco de dados.
- Meça frescor e invalidações, não apenas a porcentagem de acertos.
- Revise periodicamente chaves sem proprietário, TTLs excessivos e dependências não cobertas.
Uma estratégia confiável de invalidação de cache em PHP torna visíveis seus compromissos: o que pode ficar obsoleto, por qual intervalo, como é corrigido e o que acontece quando um componente falha. Essa clareza é mais valiosa do que adicionar um cache de forma indiscriminada.



