Load data on demand into a cache from a data store. This can improve performance and also helps to maintain consistency between data held in the cache and data in the underlying data store.
將數(shù)據(jù)從存儲加載到緩存中。 這可以提高性能,并且還有助于保持緩存中保存的數(shù)據(jù)與底層數(shù)據(jù)存儲中的數(shù)據(jù)之間的一致性。
Context and problem
Applications use a cache to improve repeated access to information held in a data store. However, it's impractical to expect that cached data will always be completely consistent with the data in the data store. 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.
應(yīng)用程序使用緩存來改進對數(shù)據(jù)存儲中保存的信息的重復(fù)訪問。 然而,期望緩存的數(shù)據(jù)將始終與數(shù)據(jù)存儲中的數(shù)據(jù)完全一致是不切實際的。 應(yīng)用程序應(yīng)實施有助于確保緩存中的數(shù)據(jù)盡可能最新的策略,并能夠檢測和處理緩存中的數(shù)據(jù)變得過時的情況。
Solution
Many commercial caching systems provideread-through and write-through/write-behind operations. In these systems, an application retrieves data by referencing the cache. If the data isn't in the cache, it's retrieved from the data store and added to the cache. Any modifications to data held in the cache are automatically written back to the data store as well.
許多商業(yè)緩存系統(tǒng)提供讀寫和寫/寫操作。 在這些系統(tǒng)中,應(yīng)用程序通過引用緩存來檢索數(shù)據(jù)。 如果數(shù)據(jù)不在緩存中,則從數(shù)據(jù)存儲中檢索數(shù)據(jù),并將其添加到緩存中。 對緩存中保存的數(shù)據(jù)的任何修改也會自動寫回數(shù)據(jù)存儲區(qū)。
For caches that don't provide this functionality, it's the responsibility of the applications that use the cache to maintain the data.
對于不提供此功能的緩存,使用緩存的應(yīng)用程序負責(zé)維護數(shù)據(jù)。
An application can emulate the functionality of read-through caching by implementing the cache-aside strategy. This strategy loads data into the cache on demand. The figure illustrates using the Cache-Aside pattern to store data in the cache.
應(yīng)用程序可以通過實現(xiàn)旁路緩存策略來模擬直讀緩存的功能。 此策略將數(shù)據(jù)按需加載到緩存中。 下圖說明了使用Cache-Aside模式將數(shù)據(jù)存儲在緩存中。

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.
如果應(yīng)用程序更新信息,則可以通過對數(shù)據(jù)存儲進行修改,并使緩存中的相應(yīng)項目無效,從而遵循直寫策略。
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.
當(dāng)下一個項目需要時,使用cache-aside策略將導(dǎo)致更新的數(shù)據(jù)從數(shù)據(jù)存儲中檢索并添加到高速緩存中。
Issues and considerations
Consider the following points when deciding how to implement this pattern:
Lifetime of cached data. Many caches implement an expiration policy that invalidates data and removes it from the cache if it's not accessed for a specified period. For cache-aside to be effective, ensure that the expiration policy matches the pattern of access for applications that use the data. 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. Similarly, don't make the expiration period so long that the cached data is likely to become stale. Remember that caching is most effective for relatively static data, or data that is read frequently.
緩存數(shù)據(jù)的生命周期。 許多高速緩存實現(xiàn)一個到期策略,使數(shù)據(jù)無效,如果在指定的時間段內(nèi)沒有訪問,則將其從緩存中刪除。 為了使緩存有效,請確保到期策略與使用數(shù)據(jù)的應(yīng)用程序的訪問模式相匹配。 不要使過期期間太短,因為這可能導(dǎo)致應(yīng)用程序不斷從數(shù)據(jù)存儲中檢索數(shù)據(jù)并將其添加到緩存。 同樣,不要讓到期期限長到緩存的數(shù)據(jù)可能變得陳舊。 請記住,緩存對于相對靜態(tài)的數(shù)據(jù)或經(jīng)常讀取的數(shù)據(jù)是最有效的。
Evicting data. Most caches have a limited size compared to the data store where the data originates, and they'll evict data if necessary. Most caches adopt a least-recently-used policy for selecting items to evict, but this might be customizable. 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. It isn't always appropriate to apply a global eviction policy to every item in the cache. 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.
數(shù)據(jù)逐出。 與數(shù)據(jù)來源的數(shù)據(jù)存儲相比,大多數(shù)緩存的大小都有限,如果需要,它們將會排除數(shù)據(jù)。 大多數(shù)緩存采用最近最少使用的策略來選擇要逐出的項目,但這應(yīng)該是可定制的。 配置緩存的全局過期屬性和其他屬性以及每個緩存項的到期屬性,以確保緩存具有成本效益。 將全局逐出策略應(yīng)用于緩存中的每個項目并不總是適合的。 例如,如果緩存項從數(shù)據(jù)存儲中檢索非常昂貴,則將該項而不是那些頻繁訪問但檢索成本較低的項保留在緩存中是有益的。
Priming the cache. Many solutions prepopulate the cache with the data that an application is likely to need as part of the startup processing. The Cache-Aside pattern can still be useful if some of this data expires or is evicted.
啟動緩存。 許多解決方案使用應(yīng)用程序可能需要的數(shù)據(jù)預(yù)先填充緩存,作為啟動處理的一部分。 如果某些數(shù)據(jù)到期或被驅(qū)逐,Cache-Aside模式仍然有用。
Consistency. Implementing the Cache-Aside pattern doesn't guarantee consistency between the data store and the cache. 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. In a system that replicates data across data stores, this problem can become serious if synchronization occurs frequently.
一致性。 實現(xiàn)Cache-Aside模式不能保證數(shù)據(jù)存儲和緩存之間的一致性。 數(shù)據(jù)存儲中的項目可以隨時通過外部進程進行更改,并且在下次加載項目前,此更改可能不會反映在緩存中。 在跨數(shù)據(jù)存儲復(fù)制數(shù)據(jù)的系統(tǒng)中,如果同步頻繁發(fā)生,則此問題可能會變得嚴重。
Local (in-memory) caching. A cache could be local to an application instance and stored in-memory. Cache-aside can be useful in this environment if an application repeatedly accesses the same data. However, a local cache is private and so different application instances could each have a copy of the same cached data. 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. In these scenarios, consider investigating the use of a shared or a distributed caching mechanism.
本地(內(nèi)存中)緩存。 緩存可以是應(yīng)用程序?qū)嵗谋镜兀⒋鎯υ趦?nèi)存中。 如果應(yīng)用程序重復(fù)訪問相同的數(shù)據(jù),緩存在此環(huán)境中可能很有用。 然而,本地緩存是私有的,因此不同的應(yīng)用程序?qū)嵗梢跃哂邢嗤彺鏀?shù)據(jù)的副本。 這些數(shù)據(jù)可能會在高速緩存之間迅速變得不一致,因此使保存在私有緩存中的數(shù)據(jù)過期,并更頻繁地刷新數(shù)據(jù)變得非常有必要。 在這些場景下,請考慮調(diào)查使用共享或分布式緩存機制。
When to use this pattern
Use this pattern when:
- A cache doesn't provide native read-through and write-through operations.
- Resource demand is unpredictable. This pattern enables applications to load data on demand. It makes no assumptions about which data an application will require in advance.
- 緩存不提供本機的直讀和直寫操作。
- 資源需求是不可預(yù)知的。 此模式使應(yīng)用程序能夠按需加載數(shù)據(jù)。 它不會提前預(yù)測應(yīng)用程序需要哪些數(shù)據(jù)。
This pattern might not be suitable:
- When the cached data set is static. 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.
- For caching session state information in a web application hosted in a web farm. In this environment, you should avoid introducing dependencies based on client-server affinity.
- 當(dāng)緩存的數(shù)據(jù)集是靜態(tài)的。 如果數(shù)據(jù)將適合可用的緩存空間,請使用啟動時的數(shù)據(jù)引導(dǎo)緩存,并應(yīng)用防止數(shù)據(jù)過期的策略。
- 用于在Web場中托管的Web應(yīng)用程序中緩存會話狀態(tài)信息。 在這種環(huán)境中,您應(yīng)該避免根據(jù)客戶端 - 服務(wù)器的親和性引入依賴關(guān)系。
Example
In Microsoft Azure you can use Azure Redis Cache to create a distributed cache that can be shared by multiple instances of an application.
To connect to an Azure Redis Cache instance, call the static Connect method and pass in the connection string. The method returns a ConnectionMultiplexer that represents the connection. 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. This approach provides a thread-safe way to initialize only a single connected instance.
在Microsoft Azure中,您可以使用Azure Redis Cache創(chuàng)建可由應(yīng)用程序的多個實例共享的分布式緩存。
要連接到Azure Redis Cache實例,請調(diào)用靜態(tài)Connect方法并傳入連接字符串。 該方法返回表示連接的ConnectionMultiplexer。 在應(yīng)用程序中共享ConnectionMultiplexer實例的一種方法是具有返回連接實例的靜態(tài)屬性,類似于以下示例。 這種方法提供了線程安全的方法來初始化一個連接的實例。
private static ConnectionMultiplexer Connection;
// Redis Connection string info
private static Lazy<ConnectionMultiplexer> lazyConnection = new Lazy<ConnectionMultiplexer>(() =>
{
string cacheConnection = ConfigurationManager.AppSettings["CacheConnection"].ToString();
return ConnectionMultiplexer.Connect(cacheConnection);
});
public static ConnectionMultiplexer Connection => lazyConnection.Value;
The GetMyEntityAsync method in the following code example shows an implementation of the Cache-Aside pattern based on Azure Redis Cache. This method retrieves an object from the cache using the read-though approach.
以下代碼示例中的GetMyEntityAsync方法顯示了基于Azure Redis Cache的Cache-Aside模式的實現(xiàn)。 此方法使用只讀方法從緩存中檢索對象。
An object is identified by using an integer ID as the key. The GetMyEntityAsync method tries to retrieve an item with this key from the cache. If a matching item is found, it's returned. 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. The code that actually reads the data from the data store is not shown here, because it depends on the data store. Note that the cached item is configured to expire to prevent it from becoming stale if it's updated elsewhere.
通過使用整數(shù)ID作為關(guān)鍵字來標識對象。
GetMyEntityAsync方法嘗試使用此鍵從緩存中檢索項目。 如果找到匹配項,則返回。 如果緩存中沒有匹配項,GetMyEntityAsync方法將從數(shù)據(jù)存儲中檢索該對象,將其添加到緩存中,然后返回。 實際上從數(shù)據(jù)存儲器讀取數(shù)據(jù)的代碼在這里不顯示,因為它取決于數(shù)據(jù)存儲。 請注意,緩存的項目被配置為過期,以防止其在其他地方更新。
// 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's 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;
}
The examples use the Azure Redis Cache API to access the store and retrieve information from the cache. For more information, see Using Microsoft Azure Redis Cache and How to create a Web App with Redis Cache
這些示例使用Azure Redis Cache API來訪問存儲并從緩存中檢索信息。 有關(guān)詳細信息,請參閱Using Microsoft Azure Redis Cache and
How to create a Web App with Redis Cache
The UpdateEntityAsync method shown below demonstrates how to invalidate an object in the cache when the value is changed by the application. This is an example of a write-through approach. The code updates the original data store and then removes the cached item from the cache by calling the KeyDeleteAsync method, specifying the key.
下面顯示的UpdateEntityAsync方法演示了當(dāng)應(yīng)用程序更改值時,如何使緩存中的對象無效。 這是一個寫通方法的例子。 代碼更新原始數(shù)據(jù)存儲,然后通過調(diào)用KeyDeleteAsync方法(指定該鍵)從緩存中刪除緩存的項目。
The order of the steps in this sequence is important. If the item is removed before the cache is updated, the client application has a short period of time to fetch the data (because it isn't found in the cache) before the item in the data store has been changed, resulting in the cache containing stale data.
這個順序的順序很重要。 如果在緩存更新之前刪除該項目,則客戶端應(yīng)用程序在數(shù)據(jù)存儲中的項目已更改之前,具有很短的時間來獲取數(shù)據(jù)(因為它在高速緩存中未找到),從而導(dǎo)致高速緩存包含陳舊的數(shù)據(jù)。
public async Task UpdateEntityAsync(MyEntity entity)
{
// Invalidate the current cache object
var cache = Connection.GetDatabase();
var id = entity.Id;
var key = $"MyEntity:{id}"; // Get the correct key for the cached object.
await cache.KeyDeleteAsync(key).ConfigureAwait(false);
// Update the object in the original data store
await this.store.UpdateEntityAsync(entity).ConfigureAwait(false);
}
Related guidance
The following information may be relevant when implementing this pattern:
Caching Guidance. 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.
Data Consistency Primer. Cloud applications typically use data that's spread across data stores. Managing and maintaining data consistency in this environment is a critical aspect of the system, particularly the concurrency and availability issues that can arise. This primer describes issues about consistency across distributed data, and summarizes how an application can implement eventual consistency to maintain the availability of data.
Caching Guidance. 提供有關(guān)如何在云解決方案中緩存數(shù)據(jù)的其他信息,以及實現(xiàn)緩存時應(yīng)考慮的問題。
Data Consistency Primer. 云應(yīng)用程序通常使用分布在數(shù)據(jù)存儲中的數(shù)據(jù)。 管理和維護此環(huán)境中的數(shù)據(jù)一致性是系統(tǒng)的一個重要方面,特別是可能出現(xiàn)的并發(fā)和可用性問題。 本引言介紹了有關(guān)分布式數(shù)據(jù)一致性的問題,并總結(jié)了應(yīng)用程序如何實現(xiàn)最終的一致性來維護數(shù)據(jù)的可用性。