Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Carregue dados a pedido para uma cache a partir de um arquivo de dados. Esta medida pode melhorar o desempenho e também ajuda a preservar a consistência entre os dados mantidos na cache e os dados no arquivo de dados subjacente.
Contexto e problema
As aplicações utilizam uma cache para melhorar o repetido acesso às informações mantidas num arquivo de dados. No entanto, é irrealista esperar que os dados armazenados em cache sejam sempre consistentes com o armazenamento de dados. Os aplicativos devem implementar uma estratégia que ajude a garantir que os dados no cache estejam o mais up-topossível atualizados. A estratégia também deve ser capaz de detetar quando os dados armazenados em cache se tornam obsoletos e tratá-los adequadamente.
Solução
Muitos sistemas de colocação em cache comerciais fornecem operações de leitura simultânea e de escrita simultânea/escrita diferida. Nestes sistemas, uma aplicação obtém os dados ao referenciar a cache. Se os dados não estiverem no cache, o aplicativo os recuperará do armazenamento de dados e os adicionará ao cache. Quaisquer modificações efetuadas aos dados mantidos na cache são automaticamente escritas no arquivo de dados também.
Para as caches que não disponibilizam esta funcionalidade, a responsabilidade pela manutenção dos dados recai sobre as aplicações que utilizam a cache.
Uma aplicação pode emular a funcionalidade de colocação em cache de leitura simultânea através da implementação da estratégia cache-aside. Esta estratégia carrega os dados para a cache a pedido. A figura abaixo ilustra a utilização do padrão Cache-Aside para armazenar dados na cache.
- O aplicativo determina se o item está atualmente mantido no cache tentando ler do cache.
- Se o item não estiver atualizado no cache (uma falha de cache), o aplicativo recuperará o item do armazenamento de dados.
- O aplicativo adiciona o item ao cache e, em seguida, o retorna ao chamador.
Se uma aplicação atualizar informações, pode seguir a estratégia de escrita simultânea ao efetuar a modificação no arquivo de dados e invalidar o item correspondente na cache.
Quando o item é necessário novamente, a estratégia cache-aside recupera os dados atualizados do armazenamento de dados e os adiciona ao cache.
Problemas e considerações
Na altura de decidir como implementar este padrão, considere os seguintes pontos:
Duração dos dados em cache. Muitos caches usam uma política de expiração para invalidar dados e removê-los do cache se não forem acessados por um período definido. Para o padrão cache-aside funcionar com eficácia, certifique-se de que a política de expiração corresponde ao padrão de acesso das aplicações que utilizam os dados. Não torne o período de expiração muito curto porque a expiração prematura pode fazer com que os aplicativos recuperem continuamente dados do armazenamento de dados e os adicionem ao cache. Do mesmo modo, não defina um período de expiração demasiado longo ao ponto de os dados em cache ficarem obsoletos. Lembre-se que a colocação em cache é mais eficaz para dados relativamente estáticos ou dados que sejam lidos com frequência.
Expulsar dados. A maioria dos caches tem um tamanho limitado em comparação com o armazenamento de dados onde os dados se originam. Se o cache exceder seu limite de tamanho, ele removerá dados. A maioria dos caches adota uma política menos usada recentemente para selecionar itens a serem removidos, mas ela pode ser personalizável.
Configuração. A configuração de cache pode ser definida globalmente e por item armazenado em cache. Uma única política global de despejo pode não se adequar a todos os itens. Uma configuração em um item de cache pode ser apropriada se um item for caro para recuperar. Nessa situação, faz sentido manter o item no cache, mesmo que ele seja acessado com menos frequência do que itens mais baratos.
Preparar a cache. Muitas soluções optam por pré-povoar a cache com os dados que uma aplicação muito provavelmente irá precisar como parte do processamento de arranque. O padrão Cache-Aside ainda pode revelar-se útil se alguns destes dados expirarem ou forem expulsos.
Consistência. A implementação do padrão Cache-Aside não garante a consistência entre o arquivo de dados e a cache. Por exemplo, um processo externo pode alterar um item no armazenamento de dados a qualquer momento. Essa alteração não aparece no cache até que o item seja carregado novamente. Em um sistema que replica dados entre armazenamentos de dados, a consistência pode ser um desafio se a sincronização ocorrer com frequência.
Colocação em cache local (na memória). Uma cache pode ser local para uma instância da aplicação e ser armazenada na memória. O padrão Cache-aside pode ser útil neste ambiente se uma determinada aplicação aceder repetidamente os mesmos dados. No entanto, uma cache local é privada, pelo que cada instância da aplicação diferente pode ter uma cópia dos mesmos dados em cache. Estes dados podem rapidamente tornar-se inconsistentes entre as caches, pelo que poderá ser necessário expirar os dados mantidos numa cache privada e atualizá-los com mais frequência. Nestes cenários, considere investigar a utilização de um mecanismo de colocação em cache distribuído ou partilhado.
Cache semântico. Algumas cargas de trabalho podem se beneficiar da recuperação de cache com base no significado semântico em vez de chaves exatas. Isso reduz o número de solicitações e tokens enviados para modelos de idioma. Certifique-se de que os dados armazenados em cache se beneficiem da equivalência semântica e não corram o risco de retornar respostas não relacionadas ou conter dados privados e confidenciais. Por exemplo, "Qual é o meu salário anual para levar para casa?" é semanticamente semelhante a "Qual é o meu salário anual para levar para casa?", mas se perguntado por dois usuários diferentes, a resposta não deve ser a mesma, nem você gostaria de incluir esses dados confidenciais em seu cache.
Quando utilizar este padrão
Utilize este padrão quando:
- Uma cache não fornece operações de leitura simultânea e de escrita simultânea nativas.
- A procura de recursos é imprevisível. Este padrão permite que as aplicações carreguem dados a pedido. Ele não faz suposições sobre quais dados um aplicativo requer com antecedência.
Este padrão pode não ser adequado:
- Se os dados forem sensíveis ou relacionados com a segurança. Pode ser inadequado armazená-lo em um cache, especialmente se o cache for compartilhado entre vários aplicativos ou usuários. Vá sempre para a fonte primária dos dados.
- Quando o conjunto de dados em cache é estático. Se os dados se encaixarem no espaço de cache disponível, prepare o cache com os dados na inicialização e aplique uma política que impeça que os dados expirem.
- Quando a maioria das solicitações não sofreria um acerto de cache. Nessa situação, a sobrecarga de verificar o cache e carregar dados nele pode superar os benefícios do cache.
- Ao armazenar em cache informações de estado da sessão em um aplicativo Web hospedado em uma web farm. Neste ambiente, deve evitar a introdução de dependências baseadas na afinidade cliente/servidor.
Design da carga de trabalho
Um arquiteto deve avaliar como o padrão Cache-Aside pode ser usado em um design para abordar as metas e os princípios abordados nos pilares do Azure Well-Architected Framework. Por exemplo:
| Pilar | Como esse padrão suporta os objetivos do pilar |
|---|---|
| As decisões de projeto de confiabilidade ajudam sua carga de trabalho a se tornar resiliente ao mau funcionamento e a garantir que ela se recupere para um estado totalmente funcional após a ocorrência de uma falha. | O cache cria replicação de dados e, de maneiras limitadas, pode ser usado para preservar a disponibilidade de dados acessados com frequência se o armazenamento de dados de origem estiver temporariamente indisponível. Além disso, se houver um mau funcionamento no cache, a carga de trabalho pode voltar para o armazenamento de dados de origem. - RE:05 Redundância |
| A Eficiência de Desempenho ajuda sua carga de trabalho a atender às demandas de forma eficiente por meio de otimizações em escala, dados e código. | O uso de uma cabine de cache melhora o desempenho para dados de leitura pesada que mudam com pouca frequência e podem tolerar algum atraso. - PE:08 Desempenho dos dados - PE:12 Otimização contínua do desempenho |
Como em qualquer decisão de design, considere quaisquer compensações em relação aos objetivos dos outros pilares que possam ser introduzidos com esse padrão.
Exemplo
Considere usar o Azure Managed Redis para criar um cache distribuído que várias instâncias de aplicativo possam compartilhar.
Este exemplo de código a seguir usa o cliente StackExchange.Redis , que é uma biblioteca de cliente Redis escrita para .NET. Para se conectar a uma instância do Azure Managed Redis, chame o método estático ConnectionMultiplexer.Connect e passe a cadeia de conexão. O método devolve um ConnectionMultiplexer que representa a ligação. Uma abordagem para partilhar uma instância ConnectionMultiplexer na sua aplicação consiste em ter uma propriedade estática que devolva uma instância ligada, tal como no seguinte exemplo. Esta abordagem fornece uma forma segura para os threads de modo a inicializar apenas uma única instância ligada.
private static ConnectionMultiplexer Connection;
// Redis connection string information
private static Lazy<ConnectionMultiplexer> lazyConnection = new Lazy<ConnectionMultiplexer>(() =>
{
string cacheConnection = ConfigurationManager.AppSettings["CacheConnection"].ToString();
return ConnectionMultiplexer.Connect(cacheConnection);
});
public static ConnectionMultiplexer Connection => lazyConnection.Value;
O GetMyEntityAsync método no exemplo de código a seguir mostra uma implementação do padrão Cache-Aside . Esse método recupera um objeto do cache usando a abordagem de leitura.
Um objeto é identificado mediante a utilização de um ID de número inteiro como chave. O método GetMyEntityAsync tenta obter um item com esta chave da cache. Se um item correspondente for encontrado, o cache o retornará. Se não for encontrada nenhuma correspondência na cache, o método GetMyEntityAsync obtém o objeto a partir de um arquivo de dados, adiciona-o à cache e, em seguida, devolve-o. O código que lê os dados do armazenamento de dados não é mostrado aqui, porque depende do armazenamento de dados. O item armazenado em cache é configurado para expirar para evitar que fique obsoleto se outro serviço ou processo o atualizar.
// Set five minute expiration as a default
private const double DefaultExpirationTimeInMinutes = 5.0;
public async Task<MyEntity> GetMyEntityAsync(int id)
{
// Define a unique key for this method and its parameters.
var key = $"MyEntity:{id}";
var cache = Connection.GetDatabase();
// Try to get the entity from the cache.
var json = await cache.StringGetAsync(key).ConfigureAwait(false);
var value = string.IsNullOrWhiteSpace(json)
? default(MyEntity)
: JsonConvert.DeserializeObject<MyEntity>(json);
if (value == null) // Cache miss
{
// If there's a cache miss, get the entity from the original store and cache it.
// Code has been omitted because it is data store dependent.
value = ...;
// Avoid caching a null value.
if (value != null)
{
// Put the item in the cache with a custom expiration time that
// depends on how critical it is to have stale data.
await cache.StringSetAsync(key, JsonConvert.SerializeObject(value)).ConfigureAwait(false);
await cache.KeyExpireAsync(key, TimeSpan.FromMinutes(DefaultExpirationTimeInMinutes)).ConfigureAwait(false);
}
}
return value;
}
Os exemplos usam o Azure Managed Redis para acessar o repositório e recuperar informações do cache. Para obter mais informações, consulte Criar um Redis Gerenciado do Azure e Usar o Azure Redis no .NET Core.
O UpdateEntityAsync método mostrado abaixo demonstra como invalidar um objeto no cache quando o aplicativo altera o valor. O código atualiza o arquivo de dados original e, em seguida, remove o item em cache da cache.
public async Task UpdateEntityAsync(MyEntity entity)
{
// Update the object in the original data store.
await this.store.UpdateEntityAsync(entity).ConfigureAwait(false);
// Invalidate the current cache object.
var cache = Connection.GetDatabase();
var id = entity.Id;
var key = $"MyEntity:{id}"; // The key for the cached object.
await cache.KeyDeleteAsync(key).ConfigureAwait(false); // Delete this key from the cache.
}
Nota
A ordem dos passos é importante. Atualize o arquivo de dados antes de remover o item da cache. Se você remover o item armazenado em cache primeiro, haverá uma pequena janela de tempo em que um cliente poderá buscar o item antes que o armazenamento de dados seja atualizado. Nessa situação, a busca resulta em uma falha de cache (porque o item foi removido do cache). A falha de cache faz com que a versão anterior do item seja buscada no armazenamento de dados e adicionada novamente ao cache. O resultado são dados de cache obsoletos.
Recursos relacionados
As seguintes informações podem ser relevantes ao implementar esse padrão:
O padrão de aplicativo Web confiável mostra como aplicar o padrão cache-aside a aplicativos Web convergentes na nuvem.
Documentação de Orientação sobre a Colocação em Cache. Fornece informações adicionais sobre como colocar em cache os dados de uma solução cloud e as questões que deve tomar em linha de conta quando implementa uma cache.
Manual Básico de Consistência de Dados. Os aplicativos em nuvem normalmente armazenam dados em vários armazenamentos de dados e locais. Gerenciar e manter a consistência dos dados nesse ambiente é um aspeto crítico do sistema, particularmente os problemas de simultaneidade e disponibilidade que podem surgir. Este manual básico descreve problemas relacionados com a consistência entre os dados distribuídos e resume a forma como uma aplicação pode implementar a consistência eventual para manter a disponibilidade dos dados.
Use o Azure Managed Redis como um cache semântico. Este tutorial mostra como implementar o cache semântico usando o Azure Managed Redis.