Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
A colocação em cache é uma técnica comum que visa melhorar o desempenho e a escalabilidade de um sistema. A cache copia temporariamente os dados frequentemente acedidos para armazenamento localizado mais próximo da aplicação do que a fonte original. Esta abordagem pode melhorar significativamente os tempos de resposta das aplicações clientes ao fornecer os dados mais rapidamente.
O cache é mais eficaz quando uma instância do cliente lê repetidamente os mesmos dados, especialmente se todas as seguintes condições se aplicarem ao armazenamento de dados original:
- O armazenamento de dados mantém-se relativamente estático.
- É lento em comparação com a velocidade da cache.
- Está sujeito a um elevado nível de contenção.
- Está suficientemente longe dos clientes para que a latência da rede seja significativa.
Armazenamento em cache em aplicativos distribuídos
Os aplicativos distribuídos normalmente implementam uma ou ambas as seguintes estratégias ao armazenar dados em cache:
Use uma cache privada, onde armazena dados localmente no computador que executa uma aplicação ou serviço.
Use uma cache partilhada, que serve como fonte comum a que múltiplos processos e máquinas podem aceder.
Em ambos os casos, pode fazer cache tanto do lado do cliente como do lado do servidor. O processo que fornece a interface de utilizador para um sistema, como um navegador web ou uma aplicação de ambiente de trabalho, gere a cache do lado do cliente. O armazenamento em cache no lado do servidor é feito remotamente pelo processo que fornece os serviços empresariais.
Cache privada
O tipo mais básico de cache é um armazenamento na memória. É mantido no espaço de endereçamento de um único processo, e o código que corre nesse processo acede diretamente à cache. Esse tipo de cache é de acesso rápido. Também pode fornecer um meio eficaz para armazenar quantidades modestas de dados estáticos. A quantidade de memória disponível na máquina normalmente limita o tamanho da cache.
Se você precisar armazenar em cache mais informações do que é fisicamente possível na memória, poderá gravar dados armazenados em cache no sistema de arquivos local. Aceder aos dados do sistema de ficheiros demora mais tempo do que se os dados estivessem armazenados na memória, mas ainda assim deverá ser mais rápido e fiável do que recuperar dados através de uma rede.
Se tiveres múltiplas instâncias de uma aplicação que usa este modelo a correr em simultâneo, cada instância tem a sua própria cache independente que guarda a sua própria cópia dos dados.
Pense em um cache como um instantâneo dos dados originais em algum momento no passado. Se esses dados não forem estáticos, é provável que instâncias de aplicativos diferentes mantenham versões diferentes dos dados em seus caches. Portanto, a mesma consulta realizada por estas instâncias pode devolver resultados diferentes, como mostrado no diagrama seguinte.
Diagrama mostrando inconsistência da cache entre múltiplas instâncias de aplicação ligadas a uma base de dados SQL partilhada. À direita está uma base de dados SQL representada como um cilindro. No canto superior esquerdo há um grande círculo rotulado com a instância de aplicação A, e no canto inferior esquerdo há um círculo de tamanho semelhante rotulado como instância de aplicação B. No interior da instância de aplicação A há um ícone de engrenagem que representa o processo de aplicação. Por baixo do ícone de engrenagem está uma tabela de grelha que representa uma cache em memória. Fora do limite, há um cache de etiquetas que lê e é um instantâneo dos dados no momento X. Dentro da instância B da aplicação há outro ícone de engrenagem. Por baixo do ícone de engrenagem está uma tabela de grelha que representa uma cache em memória. Fora do limite, há uma etiqueta com o texto: «a cache é um instantâneo dos dados no instante Y». A partir da base de dados SQL, parte uma linha em direção à instância de aplicação A, com a legenda: «a instância de aplicação A recupera os dados no instante X e coloca-os em cache na memória». Uma segunda linha estende-se da base de dados SQL para a instância de aplicação B, rotulada como instância de aplicação B, recupera dados no momento Y e armazena-os em cache na memória. Entre estas duas linhas de anotação, há uma nota com a indicação de que a informação na base de dados muda entre o momento X e o momento Y.
Cache partilhada
Ao usar uma cache partilhada, pode garantir que todas as instâncias de aplicação vejam a mesma vista dos dados em cache. A cache está localizada num local separado, que normalmente é alojado como parte de um serviço separado, como mostrado no diagrama seguinte.
Diagrama que mostra como um serviço de cache partilhado resolve inconsistências de cache entre múltiplas instâncias de aplicação ligadas a uma base de dados SQL. No extremo direito está uma base de dados SQL representada como um cilindro. No centro há uma caixa rotulada como serviço de cache partilhada, contendo uma tabela de grelha que representa a cache partilhada. No extremo esquerdo há um ícone de engrenagem rotulado instância de aplicação A. Por baixo, há um segundo ícone de engrenagem rotulado instância de aplicação B. Uma seta preta aponta da base de dados SQL para a grelha de serviço de cache partilhada, indicando que a base de dados preenche a cache partilhada. Do serviço de cache partilhado, partem duas setas azuis, uma apontada para a instância de aplicação A e outra para a instância de aplicação B. À extrema esquerda, um colchete curvo estende-se verticalmente entre as duas instâncias de aplicação, com a legenda adjacente: 'Ambas as instâncias de aplicação veem os mesmos dados em cache.' Este rótulo e colchete destacam o principal benefício da arquitetura de cache partilhada: ao contrário de uma cache em memória por instância, ambas as instâncias de aplicação A e B recuperam dados da mesma cache centralizada.
Um benefício importante da abordagem de cache partilhada é a escalabilidade. Muitos serviços de cache compartilhado são implementados usando um cluster de servidores e usam software para distribuir os dados pelo cluster de forma transparente. Uma instância de aplicação envia um pedido para o serviço de cache. A infraestrutura subjacente determina o local dos dados armazenados em cache no cluster. Você pode facilmente dimensionar o cache adicionando mais servidores.
Há duas desvantagens principais da abordagem de cache compartilhado:
- O cache é mais lento de aceder porque já não é mantido localmente com cada instância de aplicação.
- Implementar um serviço de cache separado pode adicionar complexidade à solução.
Considerações sobre o uso de cache
As secções seguintes descrevem considerações para o design e utilização de uma cache.
Decidir quando armazenar dados em cache
O cache pode melhorar drasticamente o desempenho, a escalabilidade e a disponibilidade. Quanto mais dados tiver e maior o número de utilizadores que precisam de aceder a esses dados, maiores serão os benefícios do cache. A cache reduz a latência e a contenção associadas ao tratamento de grandes volumes de pedidos concorrentes no armazenamento de dados original.
Por exemplo, um banco de dados pode suportar um número limitado de conexões simultâneas. No entanto, recuperar dados de um cache compartilhado, em vez do banco de dados subjacente, possibilita que um aplicativo cliente acesse esses dados, mesmo que o número de conexões disponíveis esteja esgotado no momento. Além disso, se a base de dados ficar indisponível, as aplicações cliente poderão continuar utilizando os dados na cache.
Considere guardar em cache dados que são lidos frequentemente mas modificados raramente. Por exemplo, dados de cache que têm uma maior proporção de operações de leitura do que operações de escrita. No entanto, não use o cache como o armazenamento autoritativo de informação crítica. Em vez disso, guarde todas as alterações importantes num armazenamento de dados persistente. Se a cache não estiver disponível, a sua aplicação pode continuar a funcionar usando o armazenamento de dados, e não perde informações importantes.
Determinar como armazenar dados em cache de forma eficaz
Para usar uma cache de forma eficaz, determine os dados mais adequados para guardar e armazene-os no momento certo. Pode adicionar dados à cache quando uma aplicação os recupera pela primeira vez. A aplicação precisa de obter os dados apenas uma vez do armazenamento de dados, e depois o acesso subsequente pode ser satisfeito usando a cache.
Em alternativa, pode preencher parcial ou totalmente uma cache com dados antecipadamente, normalmente quando a aplicação inicia. Esta abordagem é conhecida como semeadura. No entanto, pode não ser aconselhável implementar a propagação para um cache grande porque essa abordagem pode impor uma carga repentina e alta no armazenamento de dados original quando o aplicativo começa a ser executado.
Analise os padrões de utilização para decidir se deve pré-preencher total ou parcialmente a cache e escolher que dados colocar em cache. Por exemplo, pode semear a cache com dados estáticos de perfil de utilizador para clientes que usam a aplicação diariamente, mas não para clientes que a usam apenas uma vez por semana.
O cache normalmente funciona bem com dados que são imutáveis ou que mudam raramente. Os exemplos incluem informações de referência, como informações sobre produtos e preços em um aplicativo de comércio eletrônico, ou recursos estáticos compartilhados que são caros de construir. Carregar parte ou todos estes dados na cache no arranque da aplicação para minimizar a procura de recursos e melhorar o desempenho. Você também pode querer ter um processo em segundo plano que atualize periodicamente os dados de referência no cache para garantir que ele esteja up-to-date. Ou, o processo em segundo plano pode atualizar o cache quando os dados de referência são alterados.
A cache é menos útil para dados dinâmicos, embora existam algumas exceções. Para mais informações, consulte a secção Cache de dados altamente dinâmicos mais adiante neste artigo. Quando os dados originais mudam regularmente, as informações armazenadas em cache tornam-se obsoletas rapidamente ou a sobrecarga de sincronizar o cache com o armazenamento de dados original reduz a eficácia do cache.
Um cache não precisa incluir os dados completos de uma entidade. Por exemplo, se um elemento de dados representa um objeto multivalorado, como um cliente bancário que tem nome, morada e saldo da conta, alguns destes elementos podem permanecer estáticos, como o nome e a morada. Outros elementos, como o saldo da conta, podem ser mais dinâmicos. Nestas situações, pode ser útil armazenar em cache as partes estáticas dos dados e recuperar (ou calcular) apenas a informação restante quando necessário.
Realize testes de desempenho e análises de utilização para determinar se o carregamento pré-preenchido ou a pedido da cache, ou uma combinação de ambos, é apropriado. Baseie a decisão na volatilidade e no padrão de utilização dos dados. A utilização do cache e a análise de desempenho são importantes em aplicativos que enfrentam cargas pesadas e devem ser altamente escaláveis. Por exemplo, em cenários altamente escaláveis, pode semear a cache para reduzir a carga no armazenamento de dados nos horários de pico.
O cache também pode ser usado para evitar a repetição de cálculos enquanto o aplicativo está em execução. Se uma operação transforma dados ou executa um cálculo complicado, ela pode salvar os resultados da operação no cache. Se o mesmo cálculo for necessário posteriormente, a aplicação pode recuperar os resultados da cache.
Uma aplicação pode modificar dados que estão numa cache. No entanto, pense na cache como um armazenamento de dados transitório que pode desaparecer a qualquer momento. Não armazene dados valiosos apenas no cache; Certifique-se de manter as informações no armazenamento de dados original também. Esta abordagem minimiza a possibilidade de perder dados caso a cache se torne indisponível.
Mantenha a cache consistente durante as escritas
Quando a sua aplicação modifica dados que também mantém em cache, decida como a cache se mantém consistente com o sistema de referência. São comuns duas abordagens:
Invalidar ao escrever: Escreve a alteração no armazenamento de dados e depois remove a entrada de cache afetada. A leitura seguinte repreenche a cache a partir do armazenamento de dados. Esta abordagem mantém o caminho de escrita simples, mas o leitor pode experienciar um erro de cache ou ver brevemente dados obsoletos imediatamente após uma escrita. O padrão Cache-Aside descreve esta abordagem.
Write-through: Atualize o armazenamento de dados e a cache como parte da mesma operação de escrita e devolva a resposta da operação de escrita apenas depois de ambas as atualizações serem concluídas com êxito. Esta abordagem oferece aos leitores o valor atualizado imediatamente após uma escrita bem-sucedida, ao custo de uma maior latência de escrita e mais lógica de coordenação.
Para um exemplo de ponta a ponta que utiliza o Funções do Azure para coordenar atualizações write-through do Base de Dados SQL do Azure e do Azure Managed Redis, veja Write-through caching with Azure Managed Redis e Base de Dados SQL do Azure.
Utilize write-through apenas para padrões de acesso com predominância de leituras que necessitem de atualização imediata após escrita. Para dados que raramente são lidos após serem escritos ou que mudam constantemente, invalidar aquando da escrita ou ignorar a cache.
Armazene dados altamente dinâmicos em cache
Armazenar informação em rápida mudança num armazenamento de dados persistente pode impor uma sobrecarga ao sistema. Por exemplo, considere um dispositivo que relata continuamente o status ou alguma outra medição. Se uma aplicação optar por não armazenar esses dados em cache com base no facto de a informação em cache estar geralmente desatualizada, a mesma consideração pode ser válida ao armazenar e recuperar essa informação do armazenamento de dados. No tempo que demora a guardar e obter esses dados, pode mudar.
Em uma situação como essa, considere os benefícios de armazenar as informações dinâmicas diretamente no cache em vez de no armazenamento de dados persistente. Se os dados não forem críticos e não exigirem auditoria, não importa se uma alteração ocasional se perde.
Gerenciar a expiração de dados em um cache
Na maioria dos casos, uma cache guarda dados que são uma cópia dos dados do armazenamento original. Os dados no armazenamento original podem mudar depois de os guardares em cache, o que pode tornar os dados em cache obsoletos. Muitos sistemas de cache permitem configurar a cache para expirar dados, o que reduz o período em que os dados podem estar desatualizados.
Depois de os dados em cache expirarem, a cache remove-os. A aplicação recupera então novos dados do armazenamento original e pode substituir os dados expirados na cache. Você pode definir uma política de expiração padrão ao configurar o cache. Em muitos serviços de cache, também pode especificar o período de expiração para objetos individuais quando os armazena programaticamente na cache. Alguns caches permitem especificar o período de expiração como valor absoluto ou como valor móvel se o item não for acedido dentro de um tempo especificado. Essa configuração substitui qualquer política de expiração em todo o cache, mas apenas para os objetos especificados.
Note
Considere cuidadosamente o período de expiração do cache e dos objetos que ele contém. Se for demasiado curta, os objetos expiram demasiado rapidamente e os benefícios de utilizar a cache são reduzidos. Se tornar o período demasiado longo, corre o risco de os dados se tornarem obsoletos.
Se permitir que os dados permaneçam na cache durante muito tempo, a cache pode encher-se. Neste caso, quaisquer pedidos para adicionar novos itens à cache podem fazer com que a cache remova forçadamente alguns itens num processo conhecido como expulsão. Os serviços de cache normalmente removem dados com base no critério LRU (Least Recently Used - Menos Usado Recentemente), mas o utilizador pode geralmente substituir esta política e impedir que os itens sejam expulsos. No entanto, se você adotar essa abordagem, corre o risco de exceder a memória disponível no cache. Uma aplicação que tenta adicionar um item a uma cache completa falha com exceção.
Algumas implementações de cache podem fornecer outras políticas de despejo. Os tipos de políticas de despejo incluem:
- Uma política mais recentemente utilizada: Remove os itens mais recentemente usados da cache na expectativa de que os dados não serão necessários novamente.
- Uma política de primeiro a entrar, primeiro a sair: Remove primeiro os dados mais antigos da cache.
- Uma política explícita de remoção: Remove itens da cache com base num evento desencadeador, como a modificação dos dados.
Invalidar dados em um cache do lado do cliente
Os dados armazenados numa cache do lado do cliente são considerados fora do controlo do serviço que fornece os dados ao cliente. Um serviço não pode forçar diretamente um cliente a adicionar ou remover informações de um cache do lado do cliente.
Esta limitação significa que um cliente que utiliza uma cache mal configurada pode continuar a usar informação desatualizada. Por exemplo, se as políticas de expiração da cache não forem implementadas corretamente, um cliente pode usar informação desatualizada que está armazenada localmente quando a informação na fonte de dados original muda.
Se construir uma aplicação web que serve dados através de uma ligação HTTP, pode implicitamente forçar um cliente web, como um navegador ou um proxy, a buscar a informação mais recente. Podes ativar esta atualização mudando o URI sempre que atualizas o recurso. Os clientes da Web normalmente usam o URI de um recurso como a chave no cache do lado do cliente, portanto, se o URI for alterado, o cliente da Web ignorará todas as versões previamente armazenadas em cache de um recurso e buscará a nova versão.
Gerir concorrência numa cache
Muitas vezes, desenha-se caches para serem partilhados por múltiplas instâncias de uma aplicação. Cada instância de aplicação pode ler e modificar dados na cache, pelo que os mesmos problemas de concorrência que surgem em qualquer armazenamento de dados partilhado também se aplicam à cache. Numa situação em que uma aplicação precisa de modificar os dados armazenados na cache, pode ser necessário garantir que as atualizações feitas por uma instância da aplicação não sobrescribam as alterações feitas por outra instância.
Dependendo da natureza dos dados e da probabilidade de colisões, adote uma de duas abordagens à concorrência:
Otimista: Antes de a aplicação atualizar os dados, verifica se os dados na cache mudaram desde que foram recuperados. Se os dados continuarem os mesmos, a aplicação faz a alteração. Caso contrário, a aplicação decide se o atualiza. A lógica de negócio que orienta esta decisão é específica da aplicação. Esta abordagem é adequada para situações onde as atualizações são infrequentes ou onde é improvável que ocorram colisões.
Pessimista: Quando a aplicação recupera os dados, bloqueia-os na cache para impedir que outra instância os altere. Este processo garante que colisões não possam ocorrer, mas também pode bloquear outras instâncias que precisam de processar os mesmos dados. A simultaneidade pessimista pode afetar a escalabilidade de uma solução e é recomendada apenas para operações de curta duração. Essa abordagem pode ser apropriada para situações em que as colisões são mais prováveis, especialmente se um aplicativo atualizar vários itens no cache e deve garantir que essas alterações sejam aplicadas de forma consistente.
Implemente alta disponibilidade e escalabilidade e melhore o desempenho
Evite usar uma cache como repositório principal de dados. O armazenamento original de dados, a partir do qual a cache é preenchida, cumpre esta função. O armazenamento de dados original assegura a persistência dos dados.
Tenha cuidado para não introduzir dependências críticas na disponibilidade de um serviço de cache compartilhado em suas soluções. Uma aplicação deverá conseguir continuar a funcionar se a cache partilhada não estiver disponível. O aplicativo não deve parar de responder ou falhar enquanto aguarda a retomada do serviço de cache.
Portanto, o aplicativo deve estar preparado para detetar a disponibilidade do serviço de cache e voltar ao armazenamento de dados original se o cache estiver inacessível. O Circuit-Breaker pattern é útil para lidar com este cenário. O serviço que fornece a cache pode ser recuperado e, depois de ficar disponível, a cache pode voltar a ser preenchida à medida que os dados são lidos do repositório de dados original, seguindo uma estratégia como o padrão Cache-Aside pattern.
No entanto, a escalabilidade do sistema pode ser afetada se a aplicação voltar ao armazenamento de dados original quando a cache estiver temporariamente indisponível. Enquanto a cache está a ser recuperada, o armazenamento original de dados pode ser inundado com pedidos de dados, resultando em timeouts e falhas em ligações.
Considere implementar um cache local e privado em cada instância de um aplicativo, juntamente com o cache compartilhado que todas as instâncias do aplicativo acessam. Quando o aplicativo recupera um item, ele pode verificar primeiro em seu cache local, depois no cache compartilhado e, finalmente, no armazenamento de dados original. O cache local pode ser preenchido usando os dados no cache compartilhado ou no banco de dados se o cache compartilhado não estiver disponível.
Essa abordagem requer uma configuração cuidadosa para evitar que o cache local se torne muito obsoleto em relação ao cache compartilhado. No entanto, a cache local atua como um buffer se a cache partilhada for inacessível, como mostrado no diagrama seguinte.
Para oferecer suporte a caches grandes que armazenam dados de vida relativamente longa, alguns serviços de cache fornecem uma opção de alta disponibilidade que implementa failover automático se o cache ficar indisponível. Essa abordagem geralmente envolve a replicação dos dados armazenados em cache em um servidor de cache primário para um servidor de cache secundário e a alternância para o servidor secundário se o servidor primário falhar ou a conectividade for perdida.
Para reduzir a latência que resulta da escrita em múltiplos destinos, a replicação para o servidor secundário pode ocorrer de forma assíncrona quando os dados são escritos na cache do servidor principal. Essa abordagem leva à possibilidade de que algumas informações armazenadas em cache possam ser perdidas se houver uma falha, mas a proporção desses dados deve ser pequena, em comparação com o tamanho geral do cache.
Se um cache compartilhado for grande, pode ser benéfico particionar os dados armazenados em cache entre nós para reduzir as chances de contenção e melhorar a escalabilidade. Muitos caches partilhados suportam a capacidade de adicionar e remover nós dinamicamente e de reequilibrar os dados entre partições. Esta abordagem pode envolver clustering, em que a coleção de nós é apresentada às aplicações clientes como uma única cache. Internamente, no entanto, os dados são dispersos entre nós seguindo uma estratégia de distribuição predefinida que equilibra a carga uniformemente. Para obter mais informações, consulte o padrão Sharding.
O clustering também pode aumentar a disponibilidade do cache. Se um nó falhar, o restante do cache ainda estará acessível. A clusterização é frequentemente utilizada com replicação e failover. Cada nó pode ser replicado e a réplica pode ser colocada rapidamente online se o nó falhar.
Muitas operações de leitura e gravação provavelmente envolverão valores ou objetos de dados únicos. No entanto, às vezes pode ser necessário armazenar ou recuperar grandes volumes de dados rapidamente. Por exemplo, a inicialização de um cache pode envolver a gravação de centenas ou milhares de itens no cache. Um aplicativo também pode precisar recuperar um grande número de itens relacionados do cache como parte da mesma solicitação.
Muitos caches de grande escala fornecem operações em lote para esses fins. Esta funcionalidade permite que uma aplicação cliente agrupe um grande volume de itens num único pedido, o que reduz a sobrecarga de realizar um grande número de pequenos pedidos.
Cache e eventual consistência
Para que o padrão Cache-Aside funcione, a instância da aplicação que preenche a cache deve ter acesso à versão mais recente e consistente dos dados. Num sistema que implementa consistência eventual (como um armazenamento de dados replicado), esta condição pode não ser verdadeira.
Uma instância de uma aplicação pode modificar um elemento de dados e invalidar a versão em cache desse item. Outra instância da aplicação pode tentar ler este item a partir de um cache, o que causa um erro no cache. Depois lê os dados do armazenamento de dados e adiciona-os à cache. No entanto, se o armazenamento de dados não estiver totalmente sincronizado com as outras réplicas, a instância da aplicação pode ler e preencher a cache com o valor antigo.
Uma cache distribuída introduz outra camada a este problema. O teorema CAP afirma que um sistema distribuído pode fornecer no máximo duas de três garantias: consistência, disponibilidade e tolerância a partições. Como as partições de rede são inevitáveis em ambientes cloud, tem de escolher entre consistência e disponibilidade. A maioria dos caches distribuídos, incluindo o Redis, prioriza a disponibilidade e a tolerância de partições em detrimento da forte consistência. Esta prioridade significa que as leituras de uma réplica de cache podem devolver dados obsoletos durante uma partição de rede ou imediatamente após uma escrita noutro nó. Ao desenhar a sua estratégia de cache, decida quanta estagnação a sua aplicação pode tolerar e defina os valores de tempo para viver (TTL) em conformidade. Para dados que devem estar atualizados, use TTLs mais curtos ou evite completamente a cache e leia do armazenamento de dados de origem.
Para mais informações sobre o manuseamento da consistência de dados em sistemas distribuídos, consulte Considerações de dados para microserviços.
Proteja os dados armazenados em cache
Independentemente do serviço de cache que utilize, considere como proteger os dados da cache contra acessos não autorizados. Duas preocupações principais são:
- A privacidade dos dados no cache.
- A privacidade dos dados à medida que fluem entre o cache e o aplicativo que está usando o cache.
Para proteger os dados na cache, o serviço de cache pode implementar um mecanismo de autenticação que exige que as aplicações especifiquem os seguintes detalhes:
- Quais identidades podem acessar dados no cache.
- As operações de leitura e escrita que estas identidades podem realizar.
Para reduzir a sobrecarga associada à leitura e escrita de dados, depois de uma identidade obter acesso de escrita ou leitura à cache, essa identidade pode usar quaisquer dados da cache.
Se precisar de restringir o acesso a subconjuntos dos dados em cache, utilize uma das seguintes abordagens:
Divide a cache em partições usando diferentes servidores de cache. Conceda acesso às identidades apenas para as partições que lhes deve ser permitido utilizar.
Encripte os dados em cada subconjunto usando chaves diferentes. Forneça as chaves de encriptação apenas às identidades que devem ter acesso a cada subconjunto. Uma aplicação cliente pode ainda conseguir recuperar todos os dados da cache, mas só pode desencriptar os dados para os quais possui as chaves.
Você também deve proteger os dados à medida que eles entram e saem do cache. Depende das características de segurança fornecidas pela infraestrutura de rede que as aplicações clientes usam para se ligar à cache. Se a cache for implementada usando um servidor no local dentro da mesma organização que aloja as aplicações clientes, o isolamento da própria rede pode não exigir que tome mais medidas. Se a cache estiver localizada remotamente e requerer uma ligação TCP ou HTTP através de uma rede pública como a internet, considere implementar o TLS.
Implemente cache usando o Azure Managed Redis
As secções restantes deste artigo descrevem como implementar os padrões de cache usando Azure Managed Redis. O Azure Managed Redis é um serviço gerido de Redis que pode usar como cache partilhada entre instâncias de aplicação. Suporta cache de valor-chave, estruturas de dados como conjuntos, conjuntos ordenados e listas, e persistência opcional para durabilidade em reinícios.
Para informações sobre os níveis disponíveis, planeamento de capacidade, redes e detalhes de funcionalidades, consulte a documentação Azure Managed Redis.
Ligue e configure aplicações clientes
O Redis suporta aplicações cliente em muitas linguagens de programação. Para aplicações .NET, estão disponíveis várias bibliotecas cliente, cada uma adequada para diferentes cargas de trabalho Redis. A tua escolha de biblioteca depende de usares o Redis estritamente como cache ou como plataforma de dados multimodelo.
Para se ligar a um servidor Rederis, use o método estático Connect da ConnectionMultiplexer classe. A ligação que este método cria é construída para ser utilizada ao longo de toda a vida útil da aplicação cliente. Vários threads concorrentes podem usar a mesma conexão. Não restabeleça nem interrompa a ligação sempre que realizar uma operação no Redis, pois isso pode degradar o desempenho.
Para exemplos de conexão específicos por linguagem, veja Use Azure Managed Redis in .NET Core.
Escolha uma biblioteca cliente .NET
Quando usar o Azure Managed Redis para cache, utilize as seguintes bibliotecas .NET:
StackExchange.Redis: Um cliente Redis de baixo nível que oferece alto desempenho. Utilize-o quando precisar de acesso direto a comandos do Redis, operações atómicas, transações, pipelining ou scripting em Lua.
Microsoft. Extensions.Caching.StackExchangeRedis: fornece uma integração
IDistributedCachepara ASP.NET Core. Use-o para o armazenamento em cache simples de chave-valor, onde os valores são guardados como matrizes opacas de bytes. Esta abstração não expõe estruturas de dados avançadas do Redis.
Estas bibliotecas fornecem as primitivas necessárias para construir padrões comuns de cache, mas a aplicação deve implementar a própria lógica de cache.
Implementar padrões de cache
A forma mais simples de usar o Redis para cache é armazenar valores sob chaves usando o modelo chave-valor. Os valores podem ser cadeias de caracteres ou dados binários de comprimento arbitrário, tornando o Redis bem adequado para armazenar em cache objetos serializados, dados de configuração, estado de sessão ou resultados pré-computados.
Desenha cuidadosamente o teu espaço de teclas e usa teclas significativas (mas não verbosas). Por exemplo, use chaves estruturadas como customer:100 (em vez de apenas 100) para representar a chave para o cliente que tem ID 100. Este esquema permite distinguir entre valores que armazenam diferentes tipos de dados. Por exemplo, também pode usar a chave orders:100 para representar a chave da ordem que tem ID 100.
Embora as strings sejam a abordagem de cache mais comum, o Redis suporta um conjunto rico de tipos de dados nativos, como hashes, listas, conjuntos, conjuntos ordenados e fluxos, que permitem padrões de cache mais flexíveis. Para mais informações sobre os tipos de dados Rederis, consulte a documentação Redis sobre tipos de dados.
Implementar o padrão Cache-Aside
Como descrito em Determinar como armazenar dados em cache de forma eficaz, uma abordagem comum é carregar os dados na cache a pedido. O exemplo seguinte verifica primeiro a cache, recolhe da fonte de dados em caso de falha e armazena o resultado para pedidos subsequentes:
var config = new ConfigurationOptions();
// ... configure endpoint, credentials, TLS, etc.
ConnectionMultiplexer redisHostConnection = ConnectionMultiplexer.Connect(config);
IDatabase cache = redisHostConnection.GetDatabase();
async Task<string> RetrieveItemAsync(string itemKey)
{
// Attempt to retrieve the item from the Redis cache
string itemValue = await cache.StringGetAsync(itemKey);
// If the value returned is null, the item was not found in the cache
// So retrieve the item from the data source and add it to the cache
if (itemValue is null)
{
itemValue = await GetItemFromDataSourceAsync(itemKey);
await cache.StringSetAsync(itemKey, itemValue);
}
return itemValue;
}
Executar operações atomizadas e em lotes
Quando múltiplos clientes ou instâncias de aplicação partilham uma cache, é necessário evitar que atualizações simultâneas corrompam dados. As estratégias gerais de concorrência são descritas anteriormente neste artigo em Gerir a concorrência num cache . O Redis fornece vários mecanismos que implementam essas estratégias.
Operações atómicas de chave única: Os comandos Use atualizam um valor num único passo, eliminando condições de corrida que ocorrem quando
GETeSETsão emitidas separadamente.INCR,INCRBY,DECR,DECRBYincrementa ou diminui atomicamente um valor numérico. Em StackExchange.Redis, useIDatabase.StringIncrementAsynceIDatabase.StringDecrementAsync. Estes comandos são úteis para contadores, limitadores de taxa e rastreamento de quotas, onde vários clientes atualizam a mesma chave em simultâneo.GETSETatómicamente define uma chave para um novo valor e devolve o valor anterior. Em StackExchange.Redis, useIDatabase.StringGetSetAsync:string oldValue = await cache.StringGetSetAsync("data:counter", 0);
Operações multichave:
MGETeMSETler ou escrever múltiplos valores de cadeia numa única ida e volta, reduzindo a sobrecarga da rede quando é necessário trabalhar com várias chaves ao mesmo tempo. OsIDatabase.StringGetAsyncmétodos eIDatabase.StringSetAsyncestão sobrecarregados para suportar esta funcionalidade:// Create a list of key-value pairs var keysAndValues = new KeyValuePair<RedisKey, RedisValue>[] { new("data:key1", "value1"), new("data:key99", "value2"), new("data:key322", "value3") }; // Store the list of key-value pairs in the cache await cache.StringSetAsync(keysAndValues); ... // Find all values that match a list of keys RedisKey[] keys = ["data:key1", "data:key99", "data:key322"]; // Values should contain { "value1", "value2", "value3" } RedisValue[] values = await cache.StringGetAsync(keys);Transações (concorrência otimista): Pode usar o
WATCHcomando para monitorizar uma ou mais chaves antes de iniciar uma transação comMULTI/EXEC. Se alguma chave observada for alterada antes do início da transação, o Redis descarta a transação e o cliente pode tentar novamente. A biblioteca StackExchange fornece suporte para transações através daITransactioninterface.Você cria um
ITransactionobjeto usando oIDatabase.CreateTransactionmétodo. Você invoca comandos para a transação usando os métodos fornecidos peloITransactionobjeto.A
ITransactioninterface dá acesso a um conjunto de métodos semelhante aos métodos acedidos pelaIDatabaseinterface, exceto que todos os métodos são assíncronos. Isso significa que eles só são executados quando oITransaction.Executemétodo é invocado. O valor que oITransaction.Executemétodo devolve indica se a transação foi criada com sucesso (verdadeira) ou se falhou (falsa).O trecho de código a seguir mostra um exemplo que incrementa e diminui dois contadores como parte da mesma transação:
ITransaction transaction = cache.CreateTransaction(); var tx1 = transaction.StringIncrementAsync("data:counter1"); var tx2 = transaction.StringDecrementAsync("data:counter2"); bool result = await transaction.ExecuteAsync(); Console.WriteLine($"Transaction {(result ? "succeeded" : "failed")}"); if (result) { long increment = await tx1; long decrement = await tx2; Console.WriteLine($"Result of increment: {increment}"); Console.WriteLine($"Result of decrement: {decrement}"); }As transações Redis são diferentes das transações em bases de dados relacionais. O
Executemétodo coloca em fila todos os comandos que compõem a transação para serem executados e, se algum comando não for válido, a transação para. Se todos os comandos forem colocados em fila com sucesso, cada comando é executado de forma assíncrona. Se algum comando falhar, os outros ainda continuarão processando. Se precisares de verificar que um comando foi concluído com sucesso, obtém os resultados usando aResultpropriedade da tarefa correspondente, como mostrado no exemplo anterior.Scripting Lua. Para atualizações em vários passos que devem ser atómicas em várias chaves, podes executar um script Lua no servidor. O Redis executa todo o script como uma única operação, sem intercalar outros comandos.
Note
Em implementações em cluster, todas as chaves envolvidas numa transação ou num script Lua devem residir no mesmo slot de hash. Use hashtags como
customer:{123}:nameoucustomer:{123}:emailpara posicionar chaves relacionadas.
Executar operações de cache sem aguardar resposta
Quando uma atualização da cache não afeta a correção da aplicação, como incrementar um contador de visualização ou atualizar uma estatística não crítica, pode evitar esperar pela resposta do servidor. Numa operação de cache de disparar e esquecer, a sua aplicação inicia uma tarefa em segundo plano e avança sem esperar que a tarefa termine. O Redis suporta operações de disparar e esquecer, que reduzem a latência de ida e volta para o cliente, através de flags de comando:
await cache.StringSetAsync("data:key1", 99);
...
cache.StringIncrement("data:key1", flags: CommandFlags.FireAndForget);
Especificar chaves que expiram automaticamente
As estratégias de expiração descritas em Gerir a expiração de dados numa cache são implementadas no Redis através de TTLs por chave. Quando armazena um item numa cache Rederis, pode especificar um timeout após o qual o item é automaticamente removido. Também pode perguntar quanto tempo uma chave tem antes de expirar usando o TTL comando. Este comando está disponível para aplicações StackExchange através do IDatabase.KeyTimeToLive método.
O trecho de código a seguir mostra como definir um tempo de expiração de 20 segundos em uma chave e consultar o tempo de vida restante da chave:
// Add a key with an expiration time of 20 seconds
await cache.StringSetAsync("data:key1", 99, TimeSpan.FromSeconds(20));
...
// Query how much time a key has left to live
// If the key has already expired, the KeyTimeToLive function returns null
TimeSpan? expiry = cache.KeyTimeToLive("data:key1");
Também pode definir a expiração para uma data e hora específicas usando o EXPIREAT comando, que está disponível na biblioteca StackExchange como KeyExpireAsync método. Utiliza um DateTime parâmetro:
await cache.StringSetAsync("data:key1", 99);
await cache.KeyExpireAsync("data:key1",
new DateTime(2026, 9, 1, 0, 0, 0, DateTimeKind.Utc));
Tip
Pode remover manualmente um item da cache usando o DEL comando, que está disponível através da biblioteca StackExchange como IDatabase.KeyDeleteAsync método.
Quando o Redis atinge o seu limite de memória, expulsa chaves de acordo com uma política de despejo configurada. A política padrão é volatile-lru, que expulsa a chave menos usada recentemente que tem um TTL definido. Outras políticas incluem allkeys-lru, volatile-random, e noeviction (que faz com que as operações de escrita falhem quando a memória está cheia). Escolha uma política de despejo com base em se a sua aplicação utiliza TTLs de forma consistente e se prefere proteger chaves sem validade. Para mais informações, consulte Gestão de Memória.
Correlar cruzadamente itens em cache
Quando armazenas itens relacionados, muitas vezes precisas de os encontrar por relação e não apenas pela chave primária. Por exemplo, pode ter de guardar em cache publicações de blogue e precisar de responder a perguntas como "que publicações partilham a tag Y?" ou "que etiquetas pertencem ao post X?"
No Azure Managed Redis, a abordagem recomendada é usar RedisJSON e RediSearch. Armazena cada item em cache como um documento JSON com os seus metadados, depois cria um índice RediSearch sobre os campos que precisas de consultar. O RediSearch gere consultas reversas, filtragem baseada em etiquetas, consultas por intervalos e pesquisa em texto completo sem exigir que a sua aplicação mantenha estruturas de índice separadas.
Para cenários mais simples, também pode usar Redis Sets para construir índices diretos e reversos manualmente. Armazene um conjunto por publicação (contendo as suas etiquetas) e um conjunto por etiqueta (contendo os IDs das publicações):
foreach (BlogPost post in posts)
{
string postTagsKey = $"blog:posts:{post.Id}:tags";
await cache.SetAddAsync(
postTagsKey, post.Tags.Select(s => (RedisValue)s).ToArray());
foreach (var tag in post.Tags)
{
await cache.SetAddAsync($"tag:{tag}:blog:posts", post.Id);
}
}
Pode então consultar as etiquetas de uma publicação usando SetMembersAsync, encontrar etiquetas comuns entre publicações usando SetCombineAsync(SetOperation.Intersect, ...), ou encontrar todas as publicações de uma determinada etiqueta. A contrapartida é que a sua aplicação tem de manter tanto os conjuntos diretos como os inversos, o que aumenta a complexidade à medida que o número de relações cresce.
Encontrar itens acessados recentemente
Muitas aplicações precisam de acompanhar os itens mais recentemente acedidos ou visualizados. Por exemplo, um site de blogue pode mostrar as publicações mais recentemente lidas a um visitante que regressa. As Listas Redis fornecem uma forma eficiente de implementar padrões de cache baseados em recência. Os itens podem ser empurrados para qualquer extremidade da lista usando LPUSH ou RPUSH, e removidos usando LPOP ou RPOP. Use LTRIM para limitar o comprimento da lista e evitar o crescimento ilimitado de memória.
Implemente um quadro de classificações
Os Conjuntos Ordenados Redis (ZSETs) mantêm classificações ordenadas ao associar cada elemento a uma pontuação numérica. A Redis mantém a ordenação automaticamente.
ZADD é O(log N), e consultas de intervalo como ZRANGE e ZREVRANGE são O(log N + M), onde M é o número de elementos devolvidos, pelo que os conjuntos ordenados permanecem eficientes mesmo com grandes contagens de itens.
Adicionar itens a uma tabela de classificação
O exemplo seguinte demonstra como adicionar um artigo de blog e a sua pontuação a um quadro de classificação usando o ZADD comando via SortedSetAddAsync:
var db = connection.GetDatabase();
string redisKey = "blog:post_rankings";
BlogPost blogPost = ...; // The blog post being ranked
await db.SortedSetAddAsync(redisKey, blogPost.Title, blogPost.Score);
Recuperar itens classificados
Pode obter itens por ordem crescente de pontuação usando SortedSetRangeByRankWithScoresAsync:
var entries = await db.SortedSetRangeByRankWithScoresAsync(redisKey);
foreach (var entry in entries)
{
Console.WriteLine($"{entry.Element}: {entry.Score}");
}
Note
SortedSetRangeByRankAsync devolve apenas os valores dos membros, não pontuações.
Recuperar os principais N itens
Para obter os itens com a pontuação mais alta, como os 10 primeiros posts, use por ordem decrescente:
foreach (var post in await cache.SortedSetRangeByRankWithScoresAsync(
redisKey, 0, 9, Order.Descending))
{
Console.WriteLine(post);
}
Recuperar itens por intervalo de pontuação
Também pode consultar itens com base nos limites de pontuação em vez de classificação:
foreach (var post in await cache.SortedSetRangeByScoreWithScoresAsync(
redisKey, 5000, 100000))
{
Console.WriteLine(post);
}
Para evitar que um quadro de classificações cresça indefinidamente, remova entradas antigas usando SortedSetRemoveRangeByRankAsync ou usando teclas com escopo temporal (por exemplo, tabelas diárias ou semanais). Pode atualizar as pontuações atomicamente usando SortedSetIncrementAsync (ZINCRBY).
Estado da sessão em cache e saída HTML
Pode usar o Azure Managed Redis para armazenar o estado da sessão e os dados de cache de saída para aplicações ASP.NET Core e ASP.NET. Quando se mantêm os dados da sessão e a saída renderizada numa cache partilhada baseada em Redis, as aplicações correm em múltiplas instâncias, como no Serviço de Aplicações do Azure, Azure Kubernetes Service (AKS), Azure Container Apps, ou conjuntos de escala de máquinas virtuais, podem manter experiências de utilizador consistentes sem necessidade de afinidade com o servidor.
Tip
Para melhor desempenho, implemente a sua aplicação e a instância Azure Managed Redis na mesma região Azure.
ASP.NET Core
As aplicações ASP.NET Core utilizam a abstração IDistributedCache e o middleware de sessão. O Azure Managed Redis integra com IDistributedCache através do pacote Microsoft.Extensions.Caching.StackExchangeRedis.
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "<your-cache-name>.<region>.redis.azure.net:10000";
options.InstanceName = "app-cache:";
});
builder.Services.AddSession();
O middleware de cache de saída ASP.NET Core também pode usar o Redis como armazenamento de suporte distribuído, permitindo que aplicações partilhem fragmentos ou páginas renderizados entre todas as instâncias. Para mais informações, consulte ASP.NET fornecedor de cache de saída Core para Redis.
Integração com o .NET Aspire
As aplicações .NET Aspire podem usar o Aspire.Hosting.Azure.Redis pacote para declarar um recurso Azure Managed Redis no host da aplicação. Os projetos que consomem recebem automaticamente a configuração da ligação através da injeção de dependência, o que elimina a gestão manual de cadeias de conexão entre serviços.
// App host: declare the Azure Managed Redis resource
var cache = builder.AddAzureManagedRedis("cache");
builder.AddProject<Projects.ProductService>()
.WithReference(cache);
Os serviços consumidores registam a cache distribuída da mesma forma que qualquer outro IDistributedCache fornecedor. Para mais informações, consulte Começar com a integração do Redis.
Alta disponibilidade, escalabilidade e particionamento
Cada instância Azure Managed Redis usa replicação primária/réplica. O serviço monitoriza a saúde dos nós e promove automaticamente uma réplica se o primário falhar. Como a replicação é assíncrona, uma pequena quantidade de dados recentemente escritos pode ser perdida durante um failover inesperado. Para as estratégias gerais por detrás da replicação, failover e cache em camadas, consulte Implementar alta disponibilidade e escalabilidade, e melhorar o desempenho anteriormente neste artigo.
Pode combinar uma cache local em memória com o Azure Managed Redis para reduzir a latência e fornecer um plano B caso a cache partilhada esteja temporariamente inacessível. Os padrões Circuit-Breaker e Cache-Aside ajudam a gerir esta abordagem em camadas.
Para cargas de trabalho que excedam a capacidade de um único nó, o Azure Managed Redis suporta a partição (sharding) dos dados entre múltiplos nós Redis. Com ambas as políticas de clustering, os dados são automaticamente particionados entre os nós através de hashing de chave para shard, failover automático e ressincronização, e reparticionamento online (scale-out e scale-in). O Azure Managed Redis suporta duas políticas de clustering:
Política de Clustering OSS (por defeito): Os clientes comunicam diretamente com o shard adequado e seguem a semântica do Cluster Redis OSS, incluindo os redirecionamentos MOVED e ASK. Clientes conscientes do cluster, como o StackExchange.Redis, tratam estes redirecionamentos automaticamente. Esta política proporciona a menor sobrecarga de encaminhamento.
Política de Clustering Empresarial Redis: Um proxy fornece um encaminhamento transparente através de um único endpoint. Os clientes não precisam de implementar lógica consciente do cluster nem de lidar com respostas MOVED/ASK. Esta política oferece uma integração mais simples com o cliente, mas introduz uma pequena sobrecarga de encaminhamento.
Azure Managed Redis também suporta
Note
Modelos de particionamento personalizados, como o hashing do lado do cliente ou proxies não-Microsoft, são tipicamente necessários apenas em implementações Redis autogeridas em máquinas virtuais ou Kubernetes. O serviço de clusterização do Azure Managed Redis gere automaticamente o encaminhamento, failover e a redistribuição de dados.
Replicação geográfica ativa
Para disponibilidade multirregião, o Azure Managed Redis suporta a geo-replicação ativa, que liga instâncias entre regiões do Azure num único grupo de replicação. Cada instância pode gerir leituras e escritas, e muda automaticamente de sincronização. A sua aplicação é responsável por redirecionar o tráfego para uma instância saudável durante uma falha regional. Para obter mais informações, consulte Configurar a replicação geográfica ativa.
Persistência de dados
Por padrão, os dados armazenados em cache no Azure Managed Redis são mantidos na memória e podem ser perdidos se um nó reiniciar ou ocorrer uma falha. Para cargas de trabalho onde reconstruir a cache a partir do armazenamento de dados de origem seria lenta ou dispendiosa, o Azure Managed Redis suporta persistência opcional de dados:
Os instantâneos RDB (base de dados Redis) criam instantâneos periódicos de um determinado momento, guardados num disco gerido. O RDB tem um impacto mínimo no desempenho durante operações normais, mas os dados escritos desde a última captura de estado podem ser perdidos.
AOF (Ficheiro de Anexação Apenas) regista todas as operações de escrita no disco. O AOF reduz a perda potencial de dados para aproximadamente um segundo de gravações, mas produz ficheiros maiores e pode reduzir a taxa de escrita.
Podes usar tanto RDB como AOF em conjunto. O Redis carrega o snapshot do RDB no arranque e depois executa novamente o log AOF para uma recuperação quase total.
Importante
A persistência melhora a durabilidade contra falhas de nós, mas não é um mecanismo de backup ou recuperação de desastres. Para dados críticos, mantenha sempre a cópia fidedigna no seu repositório de dados de origem e use o padrão Cache-Aside para repopular a cache.
Para detalhes de configuração, consulte Configurar persistência de dados.
Proteja os dados em cache no Azure Managed Redis
A orientação em Protect cached data descreve preocupações relativas ao controlo de acesso e aos dados em trânsito. O Azure Managed Redis ajuda-o a responder a estas preocupações das seguintes formas:
Use a autenticação Microsoft Entra ID como principal mecanismo de controlo de acessos e siga o princípio do menor privilégio ao conceder acesso.
Utilize endpoints privados para restringir o acesso à rede, de modo a que o tráfego não atravesse a Internet pública.
O Azure Managed Redis encripta os dados em trânsito usando TLS e encripta os dados em repouso.
Considerações de serialização
Quando armazenas objetos .NET no Redis como valores de string, precisas de os serializar. Ao escolher um formato de serialização, considere compromissos entre desempenho, interoperabilidade, versionamento e tamanho da carga útil. Não existe um único serializador mais rápido para todos os cenários. Os benchmarks dependem muito do contexto e podem não refletir a tua carga real de trabalho.
Se a sua camada Azure Managed Redis suportar RedisJSON, poderá armazenar objetos como documentos JSON nativos e consultar campos individuais sem desserializar todo o valor:
public static class RedisJsonExtensions
{
public static async Task<T?> GetAsync<T>(
this IDatabase cache,
string key,
string path = "$")
{
var result = await cache.ExecuteAsync("JSON.GET", key, path);
if (result.IsNull)
return default;
return JsonSerializer.Deserialize<T>(result!);
}
public static async Task SetAsync<T>(
this IDatabase cache,
string key,
T value,
TimeSpan? expiry = null,
string path = "$")
{
var json = JsonSerializer.Serialize(value);
// Store JSON document
await cache.ExecuteAsync("JSON.SET", key, path, json);
// Apply TTL if provided
if (expiry.HasValue)
{
await cache.KeyExpireAsync(key, expiry);
}
}
public static async Task<bool> ExpireAsync(
this IDatabase cache,
string key,
TimeSpan expiry)
{
return await cache.KeyExpireAsync(key, expiry);
}
}
Ao serializar valores como cadeias de Redis, as opções comuns de formato incluem:
JSON - Formato legível por humanos que oferece amplo suporte multiplataforma. Não é o formato mais compacto, mas é uma boa escolha quando os itens em cache são devolvidos diretamente aos clientes HTTP porque evita uma etapa extra de desserialização e resserialização.
MessagePack - Um formato binário compacto que não exige esquema. Produz cargas úteis menores do que o JSON com menor sobrecarga de serialização.
Protocol Buffers (protobuf) - Um formato binário baseado em esquema que produz cargas úteis compactas. Requer
.protoficheiros de definição e uma etapa de compilação para gerar código específico da linguagem.BSON - Um formato binário que estende o JSON com mais tipos, como datas e dados binários brutos. As cargas úteis são comparáveis em tamanho ao de um JSON. Uma escolha prática quando a sua aplicação já usa BSON noutros locais, como no MongoDB.
Recursos relacionados
- Documentação Azure Managed Redis
- Perguntas frequentes sobre o Azure Managed Redis
- Documentação Redis
- StackExchange.Redis
Os seguintes padrões podem ser relevantes ao implementar cache nas suas aplicações:
Padrão Cache-Aside: Este padrão descreve como carregar dados, conforme necessário, para uma cache a partir de um armazenamento de dados. Também ajuda a manter a consistência entre os dados na cache e os dados do armazenamento original.
Padrão de fragmentação: Este padrão fornece informação sobre a implementação da partição horizontal para ajudar a melhorar a escalabilidade ao armazenar e aceder a grandes volumes de dados.