快取記憶體內部 ASP.NET Core

作者 :Rick AndersonJohn SteveSteve Smith

快取可藉由減少產生內容所需的工作,大幅改善應用程式的效能和延展性。 快取最適合不常變更 的資料,而且 產生成本很高。 快取可讓資料複本的傳回速度比來源快很多。 應用程式應該寫入並測試 ,永遠不會 相依于快取的資料。

ASP.NET Core支援數個不同的快取。 最簡單的快取是以 為基礎 IMemoryCacheIMemoryCache 表示儲存在網頁伺服器記憶體中的快取。 在伺服器陣列上執行的應用程式 (多部伺服器) 應該確保會話在使用記憶體內部快取時會保持黏性。 黏性會話可確保來自用戶端的要求全都移至相同的伺服器。 例如,Azure Web 應用程式會使用 應用程式要求路由 (ARR) ,將所有要求路由傳送至相同的伺服器。

Web 服務器陣列中的非黏性會話需要 分散式 快取,以避免快取一致性問題。 對於某些應用程式,分散式快取可以支援比記憶體內部快取更高的向外延展。 使用分散式快取將快取記憶體卸載至外部進程。

記憶體內部快取可以儲存任何物件。 分散式快取介面僅限於 byte[] 。 記憶體內部和分散式快取會將快取專案儲存為索引鍵/值組。

System.Runtime.Caching/MemoryCache

System.Runtime.Caching/MemoryCache (NuGet 套件) 可以搭配使用:

  • .NET Standard 2.0 或更新版本。
  • 以 .NET Standard 2.0 或更新版本為目標的任何 .NET 實 作。 例如,ASP.NET Core 3.1 或更新版本。
  • .NET Framework 4.5 或更新版本。

建議使用 /System.Runtime.CachingMemoryCache 本文中所述的Microsoft.Extensions.Caching.Memory/IMemoryCache () ,因為它更能整合到 ASP.NET Core中。 例如, IMemoryCache 以原生方式與 ASP.NET Core相依性插入搭配運作。

將程式碼從 ASP.NET 4.x 移植到 ASP.NET Core 時,請使用 System.Runtime.Caching/MemoryCache 作為相容性橋接器。

快取指導方針

  • 程式碼應該一律有後援選項來擷取資料, 而不 需依賴可用的快取值。
  • 快取會使用資源、記憶體。 限制快取成長:
    • 請勿在快取中插入外部輸入。 例如,不建議使用任意使用者提供的輸入作為快取索引鍵,因為輸入可能會耗用無法預測的記憶體數量。
    • 使用到期日來限制快取成長。
    • 使用 SetSize、Size 和 SizeLimit 來限制快取大小。 ASP.NET Core執行時間不會根據記憶體壓力來限制快取大小。 開發人員可以限制快取大小。

使用 IMemoryCache

警告

相依性插入使用共用記憶體快取,並呼叫 SetSizeSizeSizeLimit 來限制快取大小可能會導致應用程式失敗。 在快取上設定大小限制時,所有專案都必須在新增時指定大小。 這可能會導致問題,因為開發人員可能無法完全控制使用共用快取的內容。 使用 SetSizeSizeSizeLimit 來限制快取時,請建立快取單一快取。 如需詳細資訊和範例,請參閱 使用 SetSize、Size 和 SizeLimit 來限制快取大小。 共用快取是由其他架構或程式庫共用。

記憶體內部快取是使用相依性插入從應用程式參考的服務。 要求建 IMemoryCache 構函式中的 實例:

public class IndexModel : PageModel
{
    private readonly IMemoryCache _memoryCache;

    public IndexModel(IMemoryCache memoryCache) =>
        _memoryCache = memoryCache;

    // ...

下列程式碼會使用 TryGetValue 來檢查時間是否在快取中。 如果未快取時間,則會使用 建立新的專案並新增至快取 Set

public void OnGet()
{
    CurrentDateTime = DateTime.Now;

    if (!_memoryCache.TryGetValue(CacheKeys.Entry, out DateTime cacheValue))
    {
        cacheValue = CurrentDateTime;

        var cacheEntryOptions = new MemoryCacheEntryOptions()
            .SetSlidingExpiration(TimeSpan.FromSeconds(3));

        _memoryCache.Set(CacheKeys.Entry, cacheValue, cacheEntryOptions);
    }

    CacheCurrentDateTime = cacheValue;
}

在上述程式碼中,快取專案會設定為滑動到期 3 秒。 如果快取專案未存取超過三秒,則會從快取中收回。 每次存取快取專案時,它會在快取中保留 3 秒。 類別 CacheKeys 是下載範例的一部分。

目前時間和快取時間隨即顯示:

<ul>
    <li>Current Time: @Model.CurrentDateTime</li>
    <li>Cached Time: @Model.CacheCurrentDateTime</li>
</ul>

下列程式碼會 Set 使用擴充方法,在不使用 的情況下 MemoryCacheEntryOptions 快取相對時間的資料:

_memoryCache.Set(CacheKeys.Entry, DateTime.Now, TimeSpan.FromDays(1));

在上述程式碼中,快取專案會設定為一天的相對到期日。 快取專案會在一天內從快取收回,即使在這段逾時期間記憶體取它也一樣。

下列程式碼會使用 GetOrCreateGetOrCreateAsync 來快取資料。

public void OnGetCacheGetOrCreate()
{
    var cachedValue = _memoryCache.GetOrCreate(
        CacheKeys.Entry,
        cacheEntry =>
        {
            cacheEntry.SlidingExpiration = TimeSpan.FromSeconds(3);
            return DateTime.Now;
        });

    // ...
}

public async Task OnGetCacheGetOrCreateAsync()
{
    var cachedValue = await _memoryCache.GetOrCreateAsync(
        CacheKeys.Entry,
        cacheEntry =>
        {
            cacheEntry.SlidingExpiration = TimeSpan.FromSeconds(3);
            return Task.FromResult(DateTime.Now);
        });

    // ...
}

下列程式碼會呼叫 Get 以擷取快取的時間:

var cacheEntry = _memoryCache.Get<DateTime?>(CacheKeys.Entry);

下列程式碼會取得或建立具有絕對到期的快取專案:

var cachedValue = _memoryCache.GetOrCreate(
    CacheKeys.Entry,
    cacheEntry =>
    {
        cacheEntry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(20);
        return DateTime.Now;
    });

只有滑動到期的快取專案集有永遠不會過期的風險。 如果在滑動到期間隔內重複存取快取的專案,該專案永遠不會過期。 結合滑動到期與絕對到期,以確保專案到期。 如果專案在滑動到期間隔內未要求,絕對到期時間會設定專案快取的上限,同時仍允許專案稍早到期。 如果滑動到期間隔 絕對到期時間通過,則會從快取收回專案。

下列程式碼會取得或建立具有滑動 絕對到期的快取專案:

var cachedValue = _memoryCache.GetOrCreate(
    CacheKeys.CallbackEntry,
    cacheEntry =>
    {
        cacheEntry.SlidingExpiration = TimeSpan.FromSeconds(3);
        cacheEntry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(20);
        return DateTime.Now;
    });

上述程式碼保證資料不會快取超過絕對時間。

GetOrCreateGetOrCreateAsyncGet 是 類別中的 CacheExtensions 擴充方法。 這些方法會擴充 的功能 IMemoryCache

MemoryCacheEntryOptions

下列範例將:

public void OnGetCacheRegisterPostEvictionCallback()
{
    var memoryCacheEntryOptions = new MemoryCacheEntryOptions()
        .SetPriority(CacheItemPriority.NeverRemove)
        .RegisterPostEvictionCallback(PostEvictionCallback, _memoryCache);

    _memoryCache.Set(CacheKeys.CallbackEntry, DateTime.Now, memoryCacheEntryOptions);
}

private static void PostEvictionCallback(
    object cacheKey, object cacheValue, EvictionReason evictionReason, object state)
{
    var memoryCache = (IMemoryCache)state;

    memoryCache.Set(
        CacheKeys.CallbackMessage,
        $"Entry {cacheKey} was evicted: {evictionReason}.");
}

使用 SetSize、Size 和 SizeLimit 來限制快取大小

MemoryCache實例可以選擇性地指定並強制執行大小限制。 快取大小限制沒有定義的測量單位,因為快取沒有測量專案大小的機制。 如果設定快取大小限制,則所有專案都必須指定大小。 ASP.NET Core執行時間不會根據記憶體壓力來限制快取大小。 開發人員可以限制快取大小。 指定的大小是以開發人員選擇的單位為單位。

例如:

  • 如果 Web 應用程式主要是快取字串,則每個快取專案大小可能是字串長度。
  • 應用程式可以將所有專案的大小指定為 1,而大小限制是專案計數。

如果未 SizeLimit 設定,快取就會成長,而不會系結。 當系統記憶體不足時,ASP.NET Core執行時間不會修剪快取。 應用程式必須架構為:

  • 限制快取成長。
  • 呼叫 CompactRemove 可用記憶體受限時。

下列程式碼會建立相依性插入可存取的無單位固定大小 MemoryCache

public class MyMemoryCache
{
    public MemoryCache Cache { get; } = new MemoryCache(
        new MemoryCacheOptions
        {
            SizeLimit = 1024
        });
}

SizeLimit 沒有單位。 如果已設定快取大小限制,快取專案必須以其認為最適當的單位指定大小。 快取實例的所有使用者都應該使用相同的單位系統。 如果快取專案大小的總和超過 所 SizeLimit 指定的值,則不會快取專案。 如果未設定快取大小限制,則會忽略專案上設定的快取大小。

下列程式碼會 MyMemoryCache相依性插入 容器註冊:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddSingleton<MyMemoryCache>();

MyMemoryCache 會建立為了解此大小有限快取之元件的獨立記憶體快取,並知道如何適當地設定快取專案大小。

您可以使用擴充方法或 Size 屬性來設定 SetSize 快取專案的大小:

if (!_myMemoryCache.Cache.TryGetValue(CacheKeys.Entry, out DateTime cacheValue))
{
    var cacheEntryOptions = new MemoryCacheEntryOptions()
        .SetSize(1);

    // cacheEntryOptions.Size = 1;

    _myMemoryCache.Cache.Set(CacheKeys.Entry, cacheValue, cacheEntryOptions);
}

在上述程式碼中,兩行反白顯示的結果會達到設定快取專案大小相同的結果。 SetSize 為了方便起見,將呼叫鏈結至 new MemoryCacheOptions()

MemoryCache.Compact

MemoryCache.Compact 會嘗試以下列順序移除指定的快取百分比:

  • 所有過期的專案。
  • 依優先順序排序的專案。 會先移除最低優先順序專案。
  • 最近使用的物件最少。
  • 具有最早絕對到期日的專案。
  • 具有最早滑動到期日的專案。

絕對不會移除具有優先權 NeverRemove 的釘選項目。 下列程式碼會移除快取專案,並呼叫 Compact 以移除 25% 的快取專案:

_myMemoryCache.Cache.Remove(CacheKeys.Entry);
_myMemoryCache.Cache.Compact(.25);

如需詳細資訊,請參閱 GitHub 上的 Compact 來源

快取相依性

下列範例示範如何在相依專案到期時過期快取專案。 已 CancellationChangeToken 將 新增至快取的專案。 在 上 CancellationTokenSource 呼叫 時 Cancel ,會收回這兩個快取專案:

public void OnGetCacheCreateDependent()
{
    var cancellationTokenSource = new CancellationTokenSource();

    _memoryCache.Set(
        CacheKeys.DependentCancellationTokenSource,
        cancellationTokenSource);

    using var parentCacheEntry = _memoryCache.CreateEntry(CacheKeys.Parent);

    parentCacheEntry.Value = DateTime.Now;

    _memoryCache.Set(
        CacheKeys.Child,
        DateTime.Now,
        new CancellationChangeToken(cancellationTokenSource.Token));
}

public void OnGetCacheRemoveDependent()
{
    var cancellationTokenSource = _memoryCache.Get<CancellationTokenSource>(
        CacheKeys.DependentCancellationTokenSource);

    cancellationTokenSource.Cancel();
}

CancellationTokenSource使用 可讓多個快取專案以群組的形式收回。 使用上述程式 using 代碼中的模式,在範圍內建立的 using 快取專案會繼承觸發程式和到期設定。

其他注意事項

  • 到期不會在背景中發生。 沒有計時器會主動掃描快取是否有過期的專案。 快取上的任何活動 (GetSet) Remove 可以觸發過期專案的背景掃描。 (CancelAfter 上的計時器 CancellationTokenSource) 也會移除專案,並觸發掃描過期的專案。 下列範例會針對已註冊的權杖使用 CancellationTokenSource(TimeSpan) 。 當此權杖引發時,它會立即移除專案,並引發收回回呼:

    if (!_memoryCache.TryGetValue(CacheKeys.Entry, out DateTime cacheValue))
    {
        cacheValue = DateTime.Now;
    
        var cancellationTokenSource = new CancellationTokenSource(
            TimeSpan.FromSeconds(10));
    
        var cacheEntryOptions = new MemoryCacheEntryOptions()
            .AddExpirationToken(
                new CancellationChangeToken(cancellationTokenSource.Token));
    
        _memoryCache.Set(CacheKeys.Entry, cacheValue, cacheEntryOptions);
    }
    
  • 使用回呼重新填入快取專案時:

    • 多個要求可以找到空的快取索引鍵值,因為回呼尚未完成。
    • 這可能會導致數個執行緒重新填入快取的專案。
  • 當一個快取專案用來建立另一個專案時,子系會複製父專案的到期權杖和以時間為基礎的到期設定。 手動移除或更新父專案不會過期子系。

  • 用來 PostEvictionCallbacks 設定從快取收回快取專案之後,將引發的回呼。

  • 針對大部分的應用程式, IMemoryCache 會啟用 。 例如,在 中 Program.cs 呼叫 AddMvcAddControllersWithViewsAddRazorPagesAddMvcCore().AddRazorViewEngine 和許多其他 Add{Service} 方法會啟用 IMemoryCache 。 對於未呼叫上述 Add{Service} 其中一個方法的應用程式,可能需要在 中 Program.cs 呼叫 AddMemoryCache

背景快取更新

使用 背景服務IHostedService 來更新快取。 背景服務可以重新計算專案,然後只在專案就緒時將它們指派給快取。

其他資源

檢視或下載範例程式碼 (如何下載)

快取基本概念

快取可藉由減少產生內容所需的工作,大幅改善應用程式的效能和延展性。 快取最適合不常變更 的資料,而且 產生成本很高。 快取可讓資料複本的傳回速度比來源快很多。 應用程式應該寫入並測試 ,永遠不會 相依于快取的資料。

ASP.NET Core支援數個不同的快取。 最簡單的快取是以 為基礎 IMemoryCacheIMemoryCache 表示儲存在網頁伺服器記憶體中的快取。 在伺服器陣列上執行的應用程式 (多部伺服器) 應該確保會話在使用記憶體內部快取時會保持黏性。 黏性會話可確保來自用戶端的後續要求全都移至相同的伺服器。 例如,Azure Web 應用程式會使用 應用程式要求路由 (ARR) 將所有後續要求路由傳送至相同的伺服器。

Web 服務器陣列中的非黏性會話需要 分散式 快取,以避免快取一致性問題。 對於某些應用程式,分散式快取可以支援比記憶體內部快取更高的向外延展。 使用分散式快取將快取記憶體卸載至外部進程。

記憶體內部快取可以儲存任何物件。 分散式快取介面僅限於 byte[] 。 記憶體內部和分散式快取會將快取專案儲存為索引鍵/值組。

System.Runtime.Caching/MemoryCache

System.Runtime.Caching/MemoryCache (NuGet 套件) 可以搭配使用:

  • .NET Standard 2.0 或更新版本。
  • 以 .NET Standard 2.0 或更新版本為目標的任何 .NET 實 作。 例如,ASP.NET Core 3.1 或更新版本。
  • .NET Framework 4.5 或更新版本。

建議使用 /System.Runtime.CachingMemoryCache 本文中所述的Microsoft.Extensions.Caching.Memory/IMemoryCache () ,因為它更能整合到 ASP.NET Core中。 例如, IMemoryCache 以原生方式與 ASP.NET Core相依性插入搭配運作。

將程式碼從 ASP.NET 4.x 移植到 ASP.NET Core 時,請使用 System.Runtime.Caching/MemoryCache 作為相容性橋接器。

快取指導方針

  • 程式碼應該一律有後援選項來擷取資料, 而不 需依賴可用的快取值。
  • 快取會使用資源、記憶體。 限制快取成長:

使用 IMemoryCache

警告

相依性插入使用共用記憶體快取,並呼叫 SetSizeSizeSizeLimit 來限制快取大小可能會導致應用程式失敗。 在快取上設定大小限制時,所有專案都必須在新增時指定大小。 這可能會導致問題,因為開發人員可能無法完全控制使用共用快取的內容。 使用 SetSizeSizeSizeLimit 來限制快取時,請建立快取單一快取。 如需詳細資訊和範例,請參閱 使用 SetSize、Size 和 SizeLimit 來限制快取大小。 共用快取是由其他架構或程式庫共用。

記憶體內部快取是使用相依性插入從應用程式參考的服務。 要求建 IMemoryCache 構函式中的 實例:

public class HomeController : Controller
{
    private IMemoryCache _cache;

    public HomeController(IMemoryCache memoryCache)
    {
        _cache = memoryCache;
    }

下列程式碼會使用 TryGetValue 來檢查時間是否在快取中。 如果未快取時間,則會建立新的專案,並使用 新增至快取 Set 。 類別 CacheKeys 是下載範例的一部分。

public static class CacheKeys
{
    public static string Entry => "_Entry";
    public static string CallbackEntry => "_Callback";
    public static string CallbackMessage => "_CallbackMessage";
    public static string Parent => "_Parent";
    public static string Child => "_Child";
    public static string DependentMessage => "_DependentMessage";
    public static string DependentCTS => "_DependentCTS";
    public static string Ticks => "_Ticks";
    public static string CancelMsg => "_CancelMsg";
    public static string CancelTokenSource => "_CancelTokenSource";
}
public IActionResult CacheTryGetValueSet()
{
    DateTime cacheEntry;

    // Look for cache key.
    if (!_cache.TryGetValue(CacheKeys.Entry, out cacheEntry))
    {
        // Key not in cache, so get data.
        cacheEntry = DateTime.Now;

        // Set cache options.
        var cacheEntryOptions = new MemoryCacheEntryOptions()
            // Keep in cache for this time, reset time if accessed.
            .SetSlidingExpiration(TimeSpan.FromSeconds(3));

        // Save data in cache.
        _cache.Set(CacheKeys.Entry, cacheEntry, cacheEntryOptions);
    }

    return View("Cache", cacheEntry);
}

目前時間和快取時間隨即顯示:

@model DateTime?

<div>
    <h2>Actions</h2>
    <ul>
        <li><a asp-controller="Home" asp-action="CacheTryGetValueSet">TryGetValue and Set</a></li>
        <li><a asp-controller="Home" asp-action="CacheGet">Get</a></li>
        <li><a asp-controller="Home" asp-action="CacheGetOrCreate">GetOrCreate</a></li>
        <li><a asp-controller="Home" asp-action="CacheGetOrCreateAsynchronous">CacheGetOrCreateAsynchronous</a></li>
        <li><a asp-controller="Home" asp-action="CacheRemove">Remove</a></li>
        <li><a asp-controller="Home" asp-action="CacheGetOrCreateAbs">CacheGetOrCreateAbs</a></li>
        <li><a asp-controller="Home" asp-action="CacheGetOrCreateAbsSliding">CacheGetOrCreateAbsSliding</a></li>

    </ul>
</div>

<h3>Current Time: @DateTime.Now.TimeOfDay.ToString()</h3>
<h3>Cached Time: @(Model == null ? "No cached entry found" : Model.Value.TimeOfDay.ToString())</h3>

下列程式碼會 Set 使用擴充方法來快取相對時間的資料,而不需要建立 MemoryCacheEntryOptions 物件:

public IActionResult SetCacheRelativeExpiration()
{
    DateTime cacheEntry;

    // Look for cache key.
    if (!_cache.TryGetValue(CacheKeys.Entry, out cacheEntry))
    {
        // Key not in cache, so get data.
        cacheEntry = DateTime.Now;

        // Save data in cache and set the relative expiration time to one day
        _cache.Set(CacheKeys.Entry, cacheEntry, TimeSpan.FromDays(1));
    }

    return View("Cache", cacheEntry);
}

快取 DateTime 的值會在逾時期間內有要求時保留在快取中。

下列程式碼會使用 GetOrCreateGetOrCreateAsync 來快取資料。

public IActionResult CacheGetOrCreate()
{
    var cacheEntry = _cache.GetOrCreate(CacheKeys.Entry, entry =>
    {
        entry.SlidingExpiration = TimeSpan.FromSeconds(3);
        return DateTime.Now;
    });

    return View("Cache", cacheEntry);
}

public async Task<IActionResult> CacheGetOrCreateAsynchronous()
{
    var cacheEntry = await
        _cache.GetOrCreateAsync(CacheKeys.Entry, entry =>
        {
            entry.SlidingExpiration = TimeSpan.FromSeconds(3);
            return Task.FromResult(DateTime.Now);
        });

    return View("Cache", cacheEntry);
}

下列程式碼會呼叫 Get 以擷取快取的時間:

public IActionResult CacheGet()
{
    var cacheEntry = _cache.Get<DateTime?>(CacheKeys.Entry);
    return View("Cache", cacheEntry);
}

下列程式碼會取得或建立具有絕對到期日的快取專案:

public IActionResult CacheGetOrCreateAbs()
{
    var cacheEntry = _cache.GetOrCreate(CacheKeys.Entry, entry =>
    {
        entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(10);
        return DateTime.Now;
    });

    return View("Cache", cacheEntry);
}

只有滑動到期的快取專案集有永不過期的風險。 如果在滑動到期間隔內重複存取快取的專案,該專案永遠不會過期。 結合滑動到期與絕對到期,以確保專案到期。 絕對到期時間會設定專案的快取時間上限,同時如果專案在滑動到期間隔內未要求,仍允許專案稍早到期。 如果滑動到期間隔 絕對到期時間通過,則會從快取收回專案。

下列程式碼會取得或建立包含滑動 絕對到期的快取專案:

public IActionResult CacheGetOrCreateAbsSliding()
{
    var cacheEntry = _cache.GetOrCreate(CacheKeys.Entry, entry =>
    {
        entry.SetSlidingExpiration(TimeSpan.FromSeconds(3));
        entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(20);
        return DateTime.Now;
    });

    return View("Cache", cacheEntry);
}

上述程式碼保證資料不會快取超過絕對時間。

GetOrCreateGetOrCreateAsyncGet 是 類別中的 CacheExtensions 擴充方法。 這些方法可擴充 的功能 IMemoryCache

MemoryCacheEntryOptions

下列範例:

  • 設定滑動到期時間。 存取此快取專案的要求將會重設滑動到期時鐘。
  • 將快取優先順序設定為 CacheItemPriority.NeverRemove
  • 設定在 PostEvictionDelegate 從快取收回專案之後呼叫的 。 回呼是在與從快取中移除專案的程式碼不同的執行緒上執行。
public IActionResult CreateCallbackEntry()
{
    var cacheEntryOptions = new MemoryCacheEntryOptions()
        // Pin to cache.
        .SetPriority(CacheItemPriority.NeverRemove)
        // Add eviction callback
        .RegisterPostEvictionCallback(callback: EvictionCallback, state: this);

    _cache.Set(CacheKeys.CallbackEntry, DateTime.Now, cacheEntryOptions);

    return RedirectToAction("GetCallbackEntry");
}

public IActionResult GetCallbackEntry()
{
    return View("Callback", new CallbackViewModel
    {
        CachedTime = _cache.Get<DateTime?>(CacheKeys.CallbackEntry),
        Message = _cache.Get<string>(CacheKeys.CallbackMessage)
    });
}

public IActionResult RemoveCallbackEntry()
{
    _cache.Remove(CacheKeys.CallbackEntry);
    return RedirectToAction("GetCallbackEntry");
}

private static void EvictionCallback(object key, object value,
    EvictionReason reason, object state)
{
    var message = $"Entry was evicted. Reason: {reason}.";
    ((HomeController)state)._cache.Set(CacheKeys.CallbackMessage, message);
}

使用 SetSize、Size 和 SizeLimit 來限制快取大小

MemoryCache實例可以選擇性地指定並強制執行大小限制。 快取大小限制沒有定義的測量單位,因為快取沒有測量專案大小的機制。 如果已設定快取大小限制,所有專案都必須指定大小。 ASP.NET Core執行時間不會根據記憶體壓力來限制快取大小。 開發人員可以限制快取大小。 指定的大小是以開發人員選擇的單位為單位。

例如:

  • 如果 Web 應用程式主要是快取字串,則每個快取專案大小可能是字串長度。
  • 應用程式可以將所有專案的大小指定為 1,而大小限制是專案計數。

如果未 SizeLimit 設定,快取就會成長,而不會系結。 ASP.NET Core執行時間不會在系統記憶體不足時修剪快取。 應用程式必須架構為:

  • 限制快取成長。
  • 呼叫 CompactRemove 可用記憶體有限時:

下列程式碼會建立可透過相依性插入存取的無單位固定大小 MemoryCache

// using Microsoft.Extensions.Caching.Memory;
public class MyMemoryCache 
{
    public MemoryCache Cache { get; private set; }
    public MyMemoryCache()
    {
        Cache = new MemoryCache(new MemoryCacheOptions
        {
            SizeLimit = 1024
        });
    }
}

SizeLimit 沒有單位。 如果已設定快取大小限制,快取專案就必須以他們認為最適當的單位來指定大小。 快取實例的所有使用者都應該使用相同的單位系統。 如果快取專案大小的總和超過 所 SizeLimit 指定的值,則不會快取專案。 如果未設定快取大小限制,則會忽略專案上設定的快取大小。

下列程式碼會 MyMemoryCache相依性插入 容器註冊。

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
    services.AddSingleton<MyMemoryCache>();
}

MyMemoryCache 會建立為了解此大小有限快取之元件的獨立記憶體快取,並知道如何適當地設定快取專案大小。

下列程式碼使用 MyMemoryCache

public class SetSize : PageModel
{
    private MemoryCache _cache;
    public static readonly string MyKey = "_MyKey";

    public SetSize(MyMemoryCache memoryCache)
    {
        _cache = memoryCache.Cache;
    }

    [TempData]
    public string DateTime_Now { get; set; }

    public IActionResult OnGet()
    {
        if (!_cache.TryGetValue(MyKey, out string cacheEntry))
        {
            // Key not in cache, so get data.
            cacheEntry = DateTime.Now.TimeOfDay.ToString();

            var cacheEntryOptions = new MemoryCacheEntryOptions()
                // Set cache entry size by extension method.
                .SetSize(1)
                // Keep in cache for this time, reset time if accessed.
                .SetSlidingExpiration(TimeSpan.FromSeconds(3));

            // Set cache entry size via property.
            // cacheEntryOptions.Size = 1;

            // Save data in cache.
            _cache.Set(MyKey, cacheEntry, cacheEntryOptions);
        }

        DateTime_Now = cacheEntry;

        return RedirectToPage("./Index");
    }
}

快取專案的大小可由 或 SetSize 擴充方法設定 Size

public IActionResult OnGet()
{
    if (!_cache.TryGetValue(MyKey, out string cacheEntry))
    {
        // Key not in cache, so get data.
        cacheEntry = DateTime.Now.TimeOfDay.ToString();

        var cacheEntryOptions = new MemoryCacheEntryOptions()
            // Set cache entry size by extension method.
            .SetSize(1)
            // Keep in cache for this time, reset time if accessed.
            .SetSlidingExpiration(TimeSpan.FromSeconds(3));

        // Set cache entry size via property.
        // cacheEntryOptions.Size = 1;

        // Save data in cache.
        _cache.Set(MyKey, cacheEntry, cacheEntryOptions);
    }

    DateTime_Now = cacheEntry;

    return RedirectToPage("./Index");
}

MemoryCache.Compact

MemoryCache.Compact 嘗試以下列順序移除指定的快取百分比:

  • 所有過期的專案。
  • 依優先順序排序的專案。 會先移除最低優先順序專案。
  • 最近使用最少的物件。
  • 具有最早絕對到期的專案。
  • 具有最早滑動到期日的專案。

永遠不會移除具有優先權 NeverRemove 的釘選項目。 下列程式碼會移除快取專案並呼叫 Compact

_cache.Remove(MyKey);

// Remove 33% of cached items.
_cache.Compact(.33);   
cache_size = _cache.Count;

如需詳細資訊,請參閱 GitHub 上的 Compact 來源

快取相依性

下列範例示範如何在相依專案到期時過期快取專案。 會 CancellationChangeToken 加入至快取的專案。 在 上 CancellationTokenSource 呼叫 時 Cancel ,會收回這兩個快取專案。

public IActionResult CreateDependentEntries()
{
    var cts = new CancellationTokenSource();
    _cache.Set(CacheKeys.DependentCTS, cts);

    using (var entry = _cache.CreateEntry(CacheKeys.Parent))
    {
        // expire this entry if the dependant entry expires.
        entry.Value = DateTime.Now;
        entry.RegisterPostEvictionCallback(DependentEvictionCallback, this);

        _cache.Set(CacheKeys.Child,
            DateTime.Now,
            new CancellationChangeToken(cts.Token));
    }

    return RedirectToAction("GetDependentEntries");
}

public IActionResult GetDependentEntries()
{
    return View("Dependent", new DependentViewModel
    {
        ParentCachedTime = _cache.Get<DateTime?>(CacheKeys.Parent),
        ChildCachedTime = _cache.Get<DateTime?>(CacheKeys.Child),
        Message = _cache.Get<string>(CacheKeys.DependentMessage)
    });
}

public IActionResult RemoveChildEntry()
{
    _cache.Get<CancellationTokenSource>(CacheKeys.DependentCTS).Cancel();
    return RedirectToAction("GetDependentEntries");
}

private static void DependentEvictionCallback(object key, object value,
    EvictionReason reason, object state)
{
    var message = $"Parent entry was evicted. Reason: {reason}.";
    ((HomeController)state)._cache.Set(CacheKeys.DependentMessage, message);
}

CancellationTokenSource使用 可讓多個快取專案以群組的形式收回。 使用上述程式 using 代碼中的模式,在 區塊內建立的 using 快取專案將會繼承觸發程式和到期設定。

其他注意事項

  • 到期不會在背景中發生。 沒有計時器會主動掃描快取是否有過期的專案。 快取上的任何活動 (GetSet) Remove 可以觸發過期專案的背景掃描。 (CancelAfter) 上的 CancellationTokenSource 計時器也會移除專案,並觸發掃描過期的專案。 下列範例會針對已註冊的權杖使用 CancellationTokenSource(TimeSpan) 。 當此權杖引發時,它會立即移除專案,並引發收回回呼:

    public IActionResult CacheAutoExpiringTryGetValueSet()
    {
        DateTime cacheEntry;
    
        if (!_cache.TryGetValue(CacheKeys.Entry, out cacheEntry))
        {
            cacheEntry = DateTime.Now;
    
            var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
    
            var cacheEntryOptions = new MemoryCacheEntryOptions()
                .AddExpirationToken(new CancellationChangeToken(cts.Token));
    
            _cache.Set(CacheKeys.Entry, cacheEntry, cacheEntryOptions);
        }
    
        return View("Cache", cacheEntry);
    }
    
  • 使用回呼重新填入快取專案時:

    • 多個要求可以找到空白的快取索引鍵值,因為回呼尚未完成。
    • 這可能會導致數個執行緒重新填入快取的專案。
  • 當一個快取專案用來建立另一個專案時,子系會複製父專案的到期權杖和以時間為基礎的到期設定。 手動移除或更新父專案時,子系不會過期。

  • 用來 PostEvictionCallbacks 設定從快取收回快取專案之後,將引發的回呼。

  • 針對大部分的應用程式, IMemoryCache 會啟用 。 例如,在 中 ConfigureServices 呼叫 AddMvcAddControllersWithViewsAddRazorPagesAddMvcCore().AddRazorViewEngine 和許多其他 Add{Service} 方法會啟用 IMemoryCache 。 對於未呼叫上述 Add{Service} 其中一個方法的應用程式,可能需要在 中 ConfigureServices 呼叫 AddMemoryCache

背景快取更新

使用之類的 IHostedService背景服務來更新快取。 背景服務可以重新計算專案,然後只在準備好時將它們指派給快取。

其他資源