另行快取模式

Azure Cache for Redis

視需要將數據載入資料存放區中的快取。 這可以改善效能,也有助於維護保留在快取中的數據與基礎數據存放區中的數據之間的一致性。

內容和問題

應用程式會使用快取來改善重複存取資料存放區中保留的資訊。 不過,不切實際的預期快取數據一律會與數據存放區中的數據完全一致。 應用程式應該實作一種策略,協助確保快取中的數據盡可能最新,但也可以偵測並處理快取中的數據過期時所發生的情況。

解決方案

許多商業快取系統都提供讀寫/寫入後置作業。 在這些系統中,應用程式會藉由參考快取來擷取數據。 如果資料不在快取中,就會從資料存放區擷取,並加入快取。 快取中保留的任何數據修改也會自動回寫至數據存放區。

對於未提供這項功能的快取,應用程式會負責使用快取來維護數據。

應用程式可以藉由實作另行快取策略來模擬讀取快取的功能。 此策略會視需要將數據載入快取。 此圖說明如何使用 Cache-Aside 模式將資料儲存在快取中。

使用 Cache-Aside 模式將資料儲存在快取中

如果應用程式更新資訊,它可以遵循寫入策略,對數據存放區進行修改,並在快取中使對應的專案失效。

當下一個需要專案時,使用另行快取策略會導致從數據存放區擷取更新的數據,並新增回快取。

問題和考慮

決定如何實作此模式時,請考慮下列幾點:

快取數據的存留期。 許多快取會實作到期原則,使數據失效,並在未存取指定期間時從快取中移除它。 若要讓快取保留有效,請確定到期原則符合使用數據之應用程式的存取模式。 請勿讓到期期間太短,因為這可能會導致應用程式持續從數據存放區擷取數據,並將其新增至快取。 同樣地,不要讓到期期間太長,快取的數據可能會變成過時。 請記住,快取對於相對靜態的數據或經常讀取的數據最為有效。

收回數據。 相較於數據來源的數據存放區,大部分快取的大小有限,而且會在必要時收回數據。 大部分快取會採用最近使用最少的原則來選取要收回的專案,但這可能是可自定義的。 設定全域到期屬性和快取的其他屬性,以及每個快取專案的到期屬性,以確保快取符合成本效益。 不一定適合將全域收回原則套用至快取中的每個專案。 例如,如果從數據存放區擷取快取專案非常昂貴,則為了犧牲更頻繁存取但成本較低的專案,將這個專案保留在快取中會很有説明。

將快取設定為 Priming。 許多解決方案會預先填入快取,其中包含應用程式在啟動處理時可能需要的數據。 如果部分數據過期或收回,快取擱置模式仍然很有用。

一致性。 實作 Cache-Aside 模式並不保證數據存放區與快取之間的一致性。 數據存放區中的專案可以隨時由外部進程變更,而且此變更可能不會反映在快取中,直到下次載入項目為止。 在跨數據存放區複寫數據的系統中,如果經常發生同步處理,這個問題可能會變得嚴重。

本機 (記憶體內部) 快取。 快取可以是應用程式實例的本機,並儲存在記憶體中。 如果應用程式重複存取相同的數據,則快取擱置在此環境中很有用。 不過,本機快取是私人的,因此不同的應用程式實例可以有相同快取數據的複本。 此數據可能會在快取之間快速變得不一致,因此可能需要讓私人快取中保存的數據過期,並更頻繁地重新整理數據。 在這些案例中,請考慮調查共用或分散式快取機制的使用。

使用此模式的時機

當下列情況時,請使用此模式:

  • 快取不提供原生讀寫作業。
  • 資源需求無法預測。 此模式可讓應用程式視需要載入資料。 它不會預先假設應用程式需要哪些數據。

此模式可能不適合:

  • 當快取的數據集為靜態時。 如果數據符合可用的快取空間,請在啟動時使用數據來準備快取,並套用原則來防止數據過期。
  • 用於快取裝載於 Web 伺服器陣列之 Web 應用程式中的工作階段狀態資訊。 在此環境中,您應該避免引入以用戶端-伺服器親和性為基礎的相依性。

工作負載設計

架構設計人員應該評估如何在工作負載的設計中使用 Cache-Aside 模式,以解決 Azure 良好架構架構支柱涵蓋的目標和原則。 例如:

要素 此模式如何支援支柱目標
可靠性設計決策可協助工作負載復原到故障,並確保它會在發生失敗后復原到完全正常運作的狀態。 快取會建立數據復寫,而且在源數據存放區暫時無法使用時,可用來保留經常存取數據的可用性。 此外,如果快取發生問題,工作負載可以回復到源數據存放區。

- RE:05 備援
效能效率 可透過調整、數據、程式代碼的優化,有效率地協助您的工作負載 符合需求 針對不常變更的讀取繁重數據使用快取時,可以取得工作負載中更好的效能,而您的工作負載的設計目的是容許一定數量的過時。

- PE:08 數據效能
- PE:12 持續效能優化

如同任何設計決策,請考慮對其他可能以此模式導入之目標的任何取捨。

範例

在 Microsoft Azure 中,您可以使用 Azure Cache for Redis 來建立分散式快取,以供應用程式的多個實例共用。

下列程式代碼範例使用 StackExchange.Redis 用戶端,這是針對 .NET 撰寫的 Redis 用戶端連結庫。 若要連線到 Azure Cache for Redis 實例,請呼叫靜態ConnectionMultiplexer.Connect方法並傳入 連接字串。 方法會傳 ConnectionMultiplexer 回 ,表示連接。 在應用程式中共用實例的 ConnectionMultiplexer 其中一種方法是讓靜態屬性傳回連接的實例,類似下列範例。 此方法提供安全線程的方式,僅初始化單一聯機的實例。

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;

GetMyEntityAsync下列程式代碼範例中的 方法會顯示 Cache-Aside 模式的實作。 這個方法會使用讀取方法從快取擷取物件。

對像是使用整數標識碼作為索引鍵來識別。 方法 GetMyEntityAsync 會嘗試從快取擷取具有此索引鍵的專案。 如果找到相符的專案,則會傳回它。 如果快取中沒有相符專案,方法 GetMyEntityAsync 會從數據存放區擷取物件、將它新增至快取,然後傳回它。 實際從數據存放區讀取數據的程式代碼不會在此顯示,因為它取決於數據存放區。 請注意,快取的專案已設定為過期,以防止它在其他地方更新時變得過時。

// 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;
}

這些範例會使用 Azure Cache for Redis 來存取存放區,並從快取擷取資訊。 如需詳細資訊,請參閱 使用 Azure Cache for Redis如何使用 Azure Cache for Redis 建立 Web 應用程式。

UpdateEntityAsync以下顯示的方法示範如何在應用程式變更值時,使快取中的物件失效。 程序代碼會更新原始資料存放區,然後從快取中移除快取專案。

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.
}

注意

步驟的順序很重要。 先更新數據存放區 ,再 從快取中移除專案。 如果您先移除快取的專案,用戶端可能會在更新數據存放區之前擷取項目的時間範圍很小。 這會導致快取遺漏(因為專案已從快取中移除),導致從數據存放區擷取舊版專案,並新增回快取。 結果將會是過時的快取數據。

實作此模式時,可能會有下列相關信息:

  • 可靠的 Web 應用程式模式示範如何將快取保留模式 套用至聚合在雲端上的 Web 應用程式。

  • 快取指引。 提供有關如何在雲端解決方案中快取數據,以及實作快取時應考慮的問題的其他資訊。

  • 數據一致性入門。 雲端應用程式通常會使用分散於數據存放區的數據。 在此環境中管理和維護數據一致性是系統的重要層面,尤其是可能發生的並行和可用性問題。 此入門說明分散式數據之間的一致性問題,並摘要說明應用程式如何實作最終一致性,以維護數據的可用性。