Padrão Cache-AsideCache-Aside pattern

Carregar dados sob demanda em um cache de um armazenamento de dados.Load data on demand into a cache from a data store. Isso pode melhorar o desempenho e também ajuda a manter a consistência entre dados mantidos no cache e os dados no armazenamento de dados subjacente.This can improve performance and also helps to maintain consistency between data held in the cache and data in the underlying data store.

Contexto e problemaContext and problem

Os aplicativos usam um cache para melhorar o acesso repetido às informações mantidas em um armazenamento de dados.Applications use a cache to improve repeated access to information held in a data store. No entanto, não é prático esperar que os dados armazenados em cache sejam sempre completamente consistentes com os dados no armazenamento de dados.However, it's impractical to expect that cached data will always be completely consistent with the data in the data store. Os aplicativos devem implementar uma estratégia que ajude a garantir que os dados no cache sejam tão atualizados quanto possível, mas também possam detectar e lidar com situações que possam surgir quando os dados no cache se tornarem obsoletos.Applications should implement a strategy that helps to ensure that the data in the cache is as up-to-date as possible, but can also detect and handle situations that arise when the data in the cache has become stale.

SoluçãoSolution

Muitos sistemas de armazenamento em cache comerciais fornecem operações de read-through e write-through/write-behind.Many commercial caching systems provide read-through and write-through/write-behind operations. Nesses sistemas, um aplicativo recupera dados referenciando o cache.In these systems, an application retrieves data by referencing the cache. Se os dados não estiverem no cache, ele é recuperado do armazenamento de dados e adicionado ao cache.If the data isn't in the cache, it's retrieved from the data store and added to the cache. Todas as modificações nos dados mantidos no cache são automaticamente gravadas no armazenamento de dados também.Any modifications to data held in the cache are automatically written back to the data store as well.

Para caches que não têm essa funcionalidade, é de responsabilidade dos aplicativos que usam o cache manter os dados.For caches that don't provide this functionality, it's the responsibility of the applications that use the cache to maintain the data.

Um aplicativo pode emular a funcionalidade de cache de read-through implementando a estratégia de cache-aside.An application can emulate the functionality of read-through caching by implementing the cache-aside strategy. Essa estratégia carrega os dados em cache sob demanda.This strategy loads data into the cache on demand. A figura ilustra usando o padrão de Cache-Aside para armazenar dados no cache.The figure illustrates using the Cache-Aside pattern to store data in the cache.

Usar o padrão de Cache-Aside para armazenar dados no cache

Se um aplicativo atualiza as informações, ele pode seguir a estratégia de write-through modificando o armazenamento de dados e invalidando o item correspondente no cache.If an application updates information, it can follow the write-through strategy by making the modification to the data store, and by invalidating the corresponding item in the cache.

Quando o item é necessário a seguir, usar a estratégia de cache-aside fará com que os dados atualizados sejam recuperados no armazenamento de dados e adicionados novamente ao cache.When the item is next required, using the cache-aside strategy will cause the updated data to be retrieved from the data store and added back into the cache.

Problemas e consideraçõesIssues and considerations

Considere os seguintes pontos ao decidir como implementar esse padrão:Consider the following points when deciding how to implement this pattern:

Tempo de vida dos dados armazenados em cache.Lifetime of cached data. Vários caches implementam uma política de expiração que invalida os dados e os remove do cache se ele não for acessado por um período especificado.Many caches implement an expiration policy that invalidates data and removes it from the cache if it's not accessed for a specified period. Para o cache-aside ser efetivado, verifique se a política de expiração corresponde ao padrão de acesso para aplicativos que usam os dados.For cache-aside to be effective, ensure that the expiration policy matches the pattern of access for applications that use the data. Não faça o período de validade muito curto, porque isso pode fazer com que os aplicativos recuperem continuamente os dados do armazenamento de dados e adicionem-nos ao cache.Don't make the expiration period too short because this can cause applications to continually retrieve data from the data store and add it to the cache. Da mesma forma, não faça o período de validade longo demais a ponto dos dados em cache se tornarem provavelmente obsoletos.Similarly, don't make the expiration period so long that the cached data is likely to become stale. Lembre-se de que o cache é mais eficiente para dados relativamente estáticos ou dados lidos com frequência.Remember that caching is most effective for relatively static data, or data that is read frequently.

Removendo dados.Evicting data. A maioria dos caches tem um tamanho limitado em comparação com o armazenamento de dados de onde os dados são originários, e eles removerão os dados, se necessário.Most caches have a limited size compared to the data store where the data originates, and they'll evict data if necessary. A maioria dos caches adotam uma política de usados menos recentemente para selecionar itens a serem removidos, mas isso pode ser personalizável.Most caches adopt a least-recently-used policy for selecting items to evict, but this might be customizable. Configure a propriedade global de expiração e outras propriedades do cache, assim como a propriedade de expiração de cada item em cache, para garantir que o cache seja econômico.Configure the global expiration property and other properties of the cache, and the expiration property of each cached item, to ensure that the cache is cost effective. Nem sempre é apropriado aplicar uma política de remoção global para cada item no cache.It isn't always appropriate to apply a global eviction policy to every item in the cache. Por exemplo, se um item em cache for muito caro para recuperar no armazenamento de dados, pode ser benéfico manter esse item no cache e dar preferência a itens acessados com mais frequência, porém mais baratos.For example, if a cached item is very expensive to retrieve from the data store, it can be beneficial to keep this item in the cache at the expense of more frequently accessed but less costly items.

Desobstrução do cache.Priming the cache. Muitas soluções preenchem o cache com os dados que um aplicativo pode precisar como parte do processo de inicialização.Many solutions prepopulate the cache with the data that an application is likely to need as part of the startup processing. O padrão de Cache-Aside ainda pode ser útil se alguns desses dados expirar ou for removido.The Cache-Aside pattern can still be useful if some of this data expires or is evicted.

Consistência.Consistency. Implementar o padrão de Cache-Aside não garante a consistência entre o armazenamento de dados e o cache.Implementing the Cache-Aside pattern doesn't guarantee consistency between the data store and the cache. Um item no armazenamento de dados pode ser alterado a qualquer momento por um processo externo, e essa alteração não pode ser refletida no cache até a próxima vez que o item seja carregado.An item in the data store can be changed at any time by an external process, and this change might not be reflected in the cache until the next time the item is loaded. Em um sistema que replica os dados por vários armazenamentos de dados, esse problema pode se tornar sério se a sincronização ocorrer com frequência.In a system that replicates data across data stores, this problem can become serious if synchronization occurs frequently.

Armazenamento em cache local (na memória).Local (in-memory) caching. Um cache pode ser local para uma instância de aplicativo e armazenado na memória.A cache could be local to an application instance and stored in-memory. O cache-aside pode ser útil nesse ambiente, se um aplicativo acessar os mesmos dados repetidamente.Cache-aside can be useful in this environment if an application repeatedly accesses the same data. No entanto, um cache local é privado e, por isso, diferentes instâncias do aplicativo podem ter uma cópia dos mesmos dados armazenados em cache.However, a local cache is private and so different application instances could each have a copy of the same cached data. Esses dados podem rapidamente se tornar inconsistentes entre os caches, por isso é necessário expirar os dados mantidos em um cache privado e atualizá-los com mais frequência.This data could quickly become inconsistent between caches, so it might be necessary to expire data held in a private cache and refresh it more frequently. Nesses cenários, considere a possibilidade de investigar o uso de um mecanismo de cache compartilhado ou distribuído.In these scenarios, consider investigating the use of a shared or a distributed caching mechanism.

Quando usar esse padrãoWhen to use this pattern

Use esse padrão quando:Use this pattern when:

  • Um cache não fornece read-through nativo e as operações de write-through.A cache doesn't provide native read-through and write-through operations.
  • A demanda de recursos é imprevisível.Resource demand is unpredictable. Esse padrão permite que os aplicativos carreguem dados sob demanda.This pattern enables applications to load data on demand. Ele não faz nenhuma suposição sobre quais dados um aplicativo exigirá antecipadamente.It makes no assumptions about which data an application will require in advance.

Esse padrão pode não ser adequado:This pattern might not be suitable:

  • Quando o conjunto de dados armazenados em cache é estático.When the cached data set is static. Se os dados se ajustarem ao espaço em cache disponível, desobstrua o cache com os dados na inicialização e aplique uma política que impeça os dados de expirar.If the data will fit into the available cache space, prime the cache with the data on startup and apply a policy that prevents the data from expiring.
  • Para armazenar em cache informações de estado de sessão em um aplicativo Web hospedado em um web farm.For caching session state information in a web application hosted in a web farm. Nesse ambiente, você deve evitar introduzir dependências baseadas na afinidade de cliente-servidor.In this environment, you should avoid introducing dependencies based on client-server affinity.

ExemploExample

No Microsoft Azure, você pode usar o Cache do Azure para Redis para criar um cache distribuído que pode ser compartilhado por várias instâncias de um aplicativo.In Microsoft Azure you can use Azure Cache for Redis to create a distributed cache that can be shared by multiple instances of an application.

Esses exemplos de código a seguir usam o cliente StackExchange.Redis, que é uma biblioteca de cliente Redis gravada para o .NET.This following code examples use the StackExchange.Redis client, which is a Redis client library written for .NET. Para conectar-se a um cache do Azure para a instância Redis, chame o método estático ConnectionMultiplexer.Connect e passe na seqüência de conexões.To connect to an Azure Cache for Redis instance, call the static ConnectionMultiplexer.Connect method and pass in the connection string. O método retorna um ConnectionMultiplexer que representa a conexão.The method returns a ConnectionMultiplexer that represents the connection. Uma abordagem para compartilhar uma instância do ConnectionMultiplexer em seu aplicativo deve ter uma propriedade estática que retorna uma instância conectada, semelhante ao exemplo a seguir.One approach to sharing a ConnectionMultiplexer instance in your application is to have a static property that returns a connected instance, similar to the following example. Essa abordagem oferece uma maneira thread-safe de inicializar somente uma única instância conectada.This approach provides a thread-safe way to initialize only a single connected instance.

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 método GetMyEntityAsync no exemplo de código a seguir mostra uma implementação do padrão Cache-Aside.The GetMyEntityAsync method in the following code example shows an implementation of the Cache-Aside pattern. Esse método recupera um objeto do cache usando a abordagem read-through.This method retrieves an object from the cache using the read-through approach.

Um objeto é identificado usando uma ID inteira como chave.An object is identified by using an integer ID as the key. O método GetMyEntityAsync tenta recuperar um item com essa chave do cache.The GetMyEntityAsync method tries to retrieve an item with this key from the cache. Se um item correspondente for encontrado, ele é retornado.If a matching item is found, it's returned. Se não houver nenhuma correspondência no cache, o método GetMyEntityAsync recupera o objeto de um armazenamento de dados, adiciona-o ao cache e, em seguida, retorna-o.If there's no match in the cache, the GetMyEntityAsync method retrieves the object from a data store, adds it to the cache, and then returns it. O código que realmente lê os dados do armazenamento de dados não é mostrado aqui, pois ele depende do armazenamento de dados.The code that actually reads the data from the data store is not shown here, because it depends on the data store. Observe que o item de cache é configurado para expirar, a fim de impedir que ele se torne obsoleto, caso tenha se atualizado em outro lugar.Note that the cached item is configured to expire to prevent it from becoming stale if it's updated elsewhere.

// 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 Cache Do Azure para Redis para acessar a loja e recuperar informações do cache.The examples use Azure Cache for Redis to access the store and retrieve information from the cache. Para obter mais informações, consulte Usando o cache do Azure para Redis e como criar um aplicativo web com cache azure para Redis.For more information, see Using Azure Cache for Redis and How to create a Web App with Azure Cache for Redis.

O método UpdateEntityAsync mostrado a seguir demonstra como invalidar um objeto no cache quando o valor é alterado pelo aplicativo.The UpdateEntityAsync method shown below demonstrates how to invalidate an object in the cache when the value is changed by the application. O código atualiza o armazenamento de dados original e, em seguida, remove o item do cache.The code updates the original data store and then removes the cached item from the 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.
}

Observação

A ordem das etapas é importante.The order of the steps is important. Atualize o armazenamento de dados antes de remover o item do cache.Update the data store before removing the item from the cache. Se você remover o item do cache primeiro, há uma pequena janela de tempo quando um cliente pode obter o item antes de atualizar o armazenamento de dados.If you remove the cached item first, there is a small window of time when a client might fetch the item before the data store is updated. Isso resultará na perda de cache (porque o item foi removido do cache), fazendo com que a versão anterior do item seja obtida no repositório de dados e adicionado de volta ao cache.That will result in a cache miss (because the item was removed from the cache), causing the earlier version of the item to be fetched from the data store and added back into the cache. O resultado será dados obsoletos do cache.The result will be stale cache data.

As informações a seguir também podem ser relevantes ao implementar esse padrão:The following information may be relevant when implementing this pattern:

  • Orientação de cache.Caching Guidance. Fornece informações adicionais sobre como você pode armazenar dados em cache em uma solução de nuvem, e os problemas que você deve considerar ao implementar um cache.Provides additional information on how you can cache data in a cloud solution, and the issues that you should consider when you implement a cache.

  • Primer de consistência de dados.Data Consistency Primer. Os aplicativos de nuvem geralmente usam dados distribuídos entre armazenamentos de dados.Cloud applications typically use data that's spread across data stores. Gerenciar e manter a consistência dos dados nesse ambiente são um aspecto crítico do sistema, especialmente a simultaneidade e os problemas de disponibilidade que podem surgir.Managing and maintaining data consistency in this environment is a critical aspect of the system, particularly the concurrency and availability issues that can arise. Este primer descreve problemas sobre a consistência entre dados distribuídos e resume como um aplicativo pode implementar a consistência eventual para manter a disponibilidade dos dados.This primer describes issues about consistency across distributed data, and summarizes how an application can implement eventual consistency to maintain the availability of data.