Pular para o conteúdo principal

Cache-Aside

Descrição​

O Cache-Aside carrega dados no cache sob demanda a partir do armazenamento de dados de origem. É a aplicação, e não o cache, que controla o fluxo. Ela consulta o cache primeiro. Se o item estiver lá (cache hit), devolve-o na hora. Se não estiver (cache miss), busca o item no armazenamento de origem, grava-o no cache e o devolve ao chamador.

Nas escritas, a aplicação atualiza primeiro o armazenamento de origem e depois invalida (remove) a entrada correspondente no cache. Na próxima leitura, o item é recarregado com o valor atualizado. A ordem importa: se o cache for invalidado antes da escrita no armazenamento, outra leitura pode recolocar no cache o valor antigo.

O padrão serve para caches que não oferecem read-through ou write-through nativos, porque reproduz esse comportamento na própria aplicação. Ele não garante consistência forte entre cache e origem. Por isso, costuma vir acompanhado de políticas de expiração (TTL) que limitam por quanto tempo um dado desatualizado pode ficar no cache.

Problemas que resolve​

  • Latência alta e carga repetida sobre o armazenamento de dados quando os mesmos itens são lidos muitas vezes.
  • Uso de caches que não carregam nem gravam dados na origem por conta própria.
  • Demanda imprevisível, em que não se sabe de antemão quais dados serão necessários e não vale a pena pré-carregar tudo.
  • Dados desatualizados no cache após escritas, tratados com invalidação explícita e expiração.
  • Disponibilidade limitada de dados muito acessados quando a origem fica indisponível por um curto período.

Quando usar​

  • O cache não oferece operações nativas de read-through e write-through.
  • A demanda é imprevisível e os dados devem ser carregados sob demanda.
  • A carga é dominada por leituras de dados que mudam pouco e toleram algum atraso de atualização.
  • Vários processos ou instâncias da aplicação precisam compartilhar um cache distribuído (por exemplo, Azure Managed Redis).

Quando evitar​

  • O conjunto de dados é estático e cabe no cache. Nesse caso, é melhor pré-carregar tudo na inicialização e não expirar os itens.
  • A maioria das requisições resultaria em cache miss. O custo de consultar e popular o cache supera o ganho.
  • Os dados são sensíveis ou relacionados a segurança, principalmente quando o cache é compartilhado entre aplicações ou usuários.
  • O objetivo é guardar estado de sessão de uma aplicação web em várias instâncias, o que cria dependência de afinidade entre cliente e servidor.
  • Os fluxos exigem ler o próprio dado logo após escrevê-lo (read-after-write). Nesses casos, considere write-through.

Trade-offs​

GanhoCusto / Risco
Menor latência de leitura para dados acessados com frequênciaPossibilidade de ler dados desatualizados entre a escrita e a próxima recarga
Menos carga e custo no armazenamento de origemA lógica de cache fica no código da aplicação, que precisa ler, popular e invalidar o cache
Só os dados realmente usados vão para o cacheO primeiro acesso a cada item, e cada acesso após expiração ou remoção, paga o custo de um miss
Leituras frequentes continuam funcionando, de forma limitada, se a origem cair por pouco tempoMais um componente para operar, monitorar e dimensionar
Se o cache falhar, é possível recorrer à origemSe muitas chaves forem perdidas ao mesmo tempo, a origem pode receber um pico de requisições (cache stampede)
  • Expiração: ajuste o TTL ao padrão de acesso. Um TTL curto demais gera recargas constantes. Um TTL longo demais mantém dados desatualizados.
  • Remoção (eviction): o cache tem tamanho limitado e costuma remover itens por LRU. Itens caros de obter podem merecer configuração individual.
  • Pré-carga (priming): é possível aquecer o cache na inicialização. O Cache-Aside continua útil para os itens que expirarem ou forem removidos.
  • Consistência: processos externos podem alterar a origem sem invalidar o cache. Em ambientes com replicação, manter a consistência é ainda mais difícil.
  • Cache local: um cache em memória por instância gera cópias divergentes entre instâncias. Use expiração mais curta ou prefira um cache compartilhado ou distribuído.
  • Cache semântico: em cargas com modelos de linguagem, buscar respostas por similaridade de significado reduz chamadas, mas só é seguro com dados não sensíveis e realmente equivalentes.
  • Observabilidade: acompanhe a taxa de acerto (hit ratio), a latência do cache e da origem e a quantidade de itens removidos.

Diagrama​

Na leitura, a aplicação consulta o cache primeiro e, em caso de miss, busca o item na origem e o grava no cache. Na escrita, atualiza a origem antes de invalidar a entrada no cache, para que a próxima leitura traga o valor novo.

Padrões relacionados​

  • Static Content Hosting — também tira carga da aplicação e da origem ao servir conteúdo estático, muitas vezes com CDN e cache.
  • Materialized View — pré-computa visões dos dados para leitura, que podem ficar no cache para acelerar consultas.
  • CQRS — separa leituras e escritas, e o lado de leitura pode usar Cache-Aside para ganhar desempenho.
  • Sharding — particiona dados, e um cache distribuído pode seguir a mesma estratégia de particionamento.
  • Circuit Breaker — protege a aplicação quando o cache ou a origem falham e permite recorrer ao outro.

Referência​