快取可藉由減少產生內容所需的工作,大幅改善應用程式的效能和延展性。 快取最適合不常變更 且 產生成本高昂的數據。 快取讓資料副本的返回速度比直接從來源取得快得多。 應用程式應該撰寫並測試, 永遠不會 相依於快取的數據。
ASP.NET Core 支援數個不同的快取。 最簡單的快取是以IMemoryCache為基礎。
IMemoryCache
表示儲存在網頁伺服器記憶體中的快取。 在伺服器農場(多部伺服器)上執行的應用程式應該在使用記憶體內部快取時確保會話持續連線。 黏性會話可確保來自用戶端的要求全都移至相同的伺服器。 例如,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 或更新版本。
Microsoft.Extensions.Caching.Memory/IMemoryCache
(本文所述)建議使用而非System.Runtime.Caching
/MemoryCache
,因為它更能整合到 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
警告
從相依性插入使用共用記憶體快取,並呼叫SetSize
、 Size
或 SizeLimit
來限制快取大小,可能會導致應用程式失敗。 快取上設定大小限制時,所有專案都必須在新增時指定大小。 這可能會導致問題,因為開發人員可能無法完全控制使用共用快取的內容。
使用 SetSize
、Size
或 SizeLimit
來限制快取時,請建立一個快取單例以進行快取。 如需詳細資訊和範例,請參閱 使用 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 秒。 類別 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));
在上述程式代碼中,快取項目會設定為一天的相對到期日。 快取項目會在一天後從快取中移除,即使在此逾時期間已被存取。
下列程式代碼會使用 GetOrCreate 和 GetOrCreateAsync 來快取數據。
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;
});
上述程式代碼保證數據不會快取超過絕對時間。
GetOrCreate、 GetOrCreateAsync和 Get 是類別中的 CacheExtensions 擴充方法。 這些方法會擴充 IMemoryCache 的功能。
MemoryCacheEntryOptions
下列範例:
- 將快取優先權設定為 CacheItemPriority.NeverRemove。
- 設定一個PostEvictionDelegate,該PostEvictionDelegate會在項目被從快取中移除後呼叫。 在與從快取中移除項目的程式碼不同的執行緒上執行回調函數。
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 執行時不會在系統記憶體不足時清理快取。 應用程式必須架構為:
下列程式代碼會建立相MemoryCache可存取的無單位固定大小:
public class MyMemoryCache
{
public MemoryCache Cache { get; } = new MemoryCache(
new MemoryCacheOptions
{
SizeLimit = 1024
});
}
SizeLimit
沒有單位。 如果已設定快取大小限制,快取項目就必須以所認為最適當的單位來指定大小。 快取實例的所有用戶都應該使用相同的單位系統。 如果快取項目的大小總和超過 SizeLimit
指定的值,則不會快取該項目。 如果未設定任何快取大小限制,則會忽略項目上設定的快取大小。
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddSingleton<MyMemoryCache>();
MyMemoryCache
被建立為大小有限快取的獨立記憶體快取,以供元件使用,這些元件瞭解這種快取並知道如何適當地設定快取項目大小。
快取專案的大小可以使用 SetSize 擴充方法或 Size 屬性來設定。
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 來源。
(No changes necessary, so the improved translation remains the same as the original.) 快取相依性
下列範例示範如果相依專案到期,如何讓快取專案過期。 一個 CancellationChangeToken 被加入至快取的專案。 當在Cancel
上呼叫CancellationTokenSource
時,這兩個快取項目都會被移除:
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
範圍內建立的快取項目會繼承觸發條件和到期設定。
其他注意事項
到期不會在背景發生。 沒有定時器會主動掃描快取中的過期項目。 快取(
Get
,TryGetValue
,Set
,Remove
)上的任何活動都可以觸發過期項目的背景掃描。CancellationTokenSource
(CancelAfter)上的一個定時器也會移除項目,並觸發掃描已過期的項目。 下列範例會針對已註冊的權杖使用 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)) .RegisterPostEvictionCallback((key, value, reason, state) => { ((CancellationTokenSource)state).Dispose(); }, cancellationTokenSource); _memoryCache.Set(CacheKeys.Entry, cacheValue, cacheEntryOptions); }
使用回呼重新填入快取項目時:
- 多個要求可能會發現快取索引鍵值為空,因為回呼尚未完成。
- 這可能會導致數個執行緒重新填入快取的項目。
當一個快取項目用來建立另一個快取項目時,子項目會複製父項目的到期令牌和以時間為基礎的到期設定。 子項不會因為手動移除或更新父項而過期。
使用 PostEvictionCallbacks 設定快取條目在被逐出後將執行的回呼函式。
針對大部分的應用程式,
IMemoryCache
會啟用 。 例如,呼叫AddMvc
中的AddControllersWithViews
、AddRazorPages
、AddMvcCore().AddRazorViewEngine
、Add{Service}
和許多其他Program.cs
方法,可以啟用IMemoryCache
。 對於未呼叫上述Add{Service}
其中一個方法的應用程式,可能需要在AddMemoryCache中呼叫Program.cs
。
背景快取更新
使用 背景服務 ,例如 IHostedService 更新快取。 背景服務可以重新計算條目,然後僅在它們準備好時再指派給快取。
其他資源
檢視或下載範例程式碼 \(英文\) (如何下載)
快取基本概念
快取可藉由減少產生內容所需的工作,大幅改善應用程式的效能和延展性。 快取最適合不常變更 且 產生成本高昂的數據。 快取讓資料副本的返回速度比直接從來源取得快得多。 應用程式應該撰寫並測試, 永遠不會 相依於快取的數據。
ASP.NET Core 支援數個不同的快取。 最簡單的快取是以IMemoryCache為基礎。
IMemoryCache
表示儲存在網頁伺服器記憶體中的快取。 在伺服器農場(多部伺服器)上執行的應用程式應該在使用記憶體內部快取時確保會話持續連線。 黏滯會話可確保來自客戶端的後續要求全都發送到相同的伺服器。 例如,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 或更新版本。
Microsoft.Extensions.Caching.Memory/IMemoryCache
(本文所述)建議使用而非System.Runtime.Caching
/MemoryCache
,因為它更能整合到 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
警告
從相依性插入使用共用記憶體快取,並呼叫SetSize
、 Size
或 SizeLimit
來限制快取大小,可能會導致應用程式失敗。 快取上設定大小限制時,所有專案都必須在新增時指定大小。 這可能會導致問題,因為開發人員可能無法完全控制使用共用快取的內容。
使用 SetSize
、Size
或 SizeLimit
來限制快取時,請建立一個快取單例以進行快取。 如需詳細資訊和範例,請參閱 使用 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
值會保留在快取中。
下列程式代碼會使用 GetOrCreate 和 GetOrCreateAsync 來快取數據。
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);
}
上述程式碼保證數據不會快取超過指定的時間。
GetOrCreate、 GetOrCreateAsync和 Get 是類別中的 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 執行時不會在系統記憶體不足時清理快取。 應用程式必須架構為:
下列程式代碼會建立相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
指定的值,該專案將不被快取。 如果未設定任何快取大小限制,則會忽略項目上設定的快取大小。
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");
}
}
快取項目的大小可以透過 Size 或 SetSize 擴充方法設定。
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 來源。
(No changes necessary, so the improved translation remains the same as the original.) 快取相依性
下列範例示範如果相依專案到期,如何讓快取專案過期。 一個 CancellationChangeToken 被加入至快取的專案。 當對Cancel
呼叫CancellationTokenSource
時,這兩個快取條目都會被移除。
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
區塊內建立的快取項目將會繼承觸發條件和到期設定。
其他注意事項
到期不會在背景發生。 沒有定時器會主動掃描快取以檢查是否有過期的項目。 快取(
Get
、Set
、Remove
)上的任何活動都可以觸發過期項目的背景掃描。CancellationTokenSource
(CancelAfter)上的一個定時器也會移除項目,並觸發掃描已過期的項目。 下列範例會針對已註冊的權杖使用 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 設定快取條目在被逐出後將執行的回呼函式。 在範例程式碼中,CancellationTokenSource.Dispose() 被呼叫來釋放
CancellationTokenSource
所使用的非受控資源。 不過,CancellationTokenSource
不會立即處置 ,因為它仍由快取專案使用。CancellationToken
會傳遞至MemoryCacheEntryOptions
,以建立在特定時間後過期的快取條目。 因此,在移除或過期快取條目之前,不應呼叫Dispose
。 範例程式碼會呼叫 RegisterPostEvictionCallback 方法,以註冊一個回呼,在快取項目被逐出時叫用,並在該回呼中釋放CancellationTokenSource
。針對大部分的應用程式,
IMemoryCache
會啟用 。 例如,呼叫AddMvc
中的AddControllersWithViews
、AddRazorPages
、AddMvcCore().AddRazorViewEngine
、Add{Service}
和許多其他ConfigureServices
方法,可以啟用IMemoryCache
。 對於未呼叫上述Add{Service}
方法之一的應用程式,可能需要在AddMemoryCache中呼叫ConfigureServices
。
背景快取更新
使用 背景服務 ,例如 IHostedService 更新快取。 背景服務可以重新計算條目,然後僅在它們準備好時再指派給快取。