以空間換時間:將昂貴計算結果(資料庫查詢、複雜運算、外部 API 呼叫)存入記憶體,設定過期策略,後續請求直接讀取,大幅降低延遲與資料庫壓力。典型場景:首頁熱門商品、分類導航、權限矩陣、報表資料。
| 類型 | 範圍 | 宣告方式 | 適用場景 |
|---|---|---|---|
| 資料快取 | 應用程式域 (AppDomain) | Cache["key"] = obj / Cache.Insert | 共用商業邏輯資料、查詢結果、計算結果 |
| 頁面輸出快取 | 單一 .aspx 頁面輸出 HTML | <%@ OutputCache %> 指示詞 | 靜態化程度高、個人化低的頁面(首頁、列表頁) |
| 控件快取 | UserControl (.ascx) 輸出 | <%@ OutputCache %> 或 VaryByControl | 頁面局部片段(側邊欄、推薦區塊) |
// 寫入(無過期,永久駐留直到記憶體壓力移除)
Cache["LastUpdate"] = DateTime.Now;
// 讀取
if (Cache["LastUpdate"] != null)
Response.Write((DateTime)Cache["LastUpdate"]);
Cache.Insert(
key: "ProductList",
value: productList,
dependencies: new CacheDependency(Server.MapPath("~/App_Data/products.xml")), // 檔案變更自動失效
absoluteExpiration: DateTime.Now.AddMinutes(30), // 絕對過期:30 分鐘後必失效
slidingExpiration: Cache.NoSlidingExpiration, // 滑動過期:不使用
priority: CacheItemPriority.Normal, // 記憶體壓力下淘汰優先序
onRemoveCallback: CacheItemRemovedCallback // 移除回調
);
Low、Normal、High、NotRemovable,記憶體不足時優先淘汰低優先序。
public void CacheItemRemovedCallback(string key, object value, CacheItemRemovedReason reason)
{
// reason: Expired, Removed, Underused, DependencyChanged
File.AppendAllText(Server.MapPath("~/App_Data/cache.log"),
$"{DateTime.Now}: Key={key}, Reason={reason}{Environment.NewLine}");
}
<%@ OutputCache
Duration="60" <!-- 快取秒數 -->
VaryByParam="categoryId;page" <!-- 依查詢字串參數區分快取版本 -->
VaryByControl="ddlCategory" <!-- 依控制項值區分(控件快取) -->
VaryByCustom="browser" <!-- 依自訂字串(需 Global.asax GetVaryByCustomString) -->
Location="Server" <!-- Server/Client/Downstream/None -->
NoStore="false" <!-- true=不快取任何位置 -->
SqlDependency="CommandNotification" <!-- SQL 快取相依性 -->
%>
<%@ OutputCache Duration="120" VaryByParam="none" Shared="true" %>
<!-- Shared=true:多頁面共用同一 UserControl 快取 -->
<%@ OutputCache Duration="30" VaryByControl="ddlRegion" %>
<asp:DropDownList ID="ddlRegion" runat="server" AutoPostBack="true" />
不同下拉選單值產生不同快取版本。
<system.web>
<caching>
<outputCacheSettings>
<outputCacheProfiles>
<add name="HomePageCache" duration="300" varyByParam="none" location="Server" />
<add name="ProductListCache" duration="60" varyByParam="categoryId" />
</outputCacheProfiles>
</outputCacheSettings>
<sqlCacheDependency enabled="true" pollTime="5000">
<databases>
<add name="AppDb" connectionStringName="AppDb" />
</databases>
</sqlCacheDependency>
</caching>
</system.web>
頁面套用:<%@ OutputCache CacheProfile="HomePageCache" %>
<system.webServer>
<caching enabled="true" enableKernelCache="true">
<profiles>
<add extension=".aspx" policy="CacheForTimePeriod" duration="00:00:10" varyByQueryString="*" kernelCachePolicy="CacheUntilChange" />
<add extension=".jpg" policy="CacheUntilChange" kernelCachePolicy="CacheUntilChange" />
<add extension=".css" policy="CacheUntilChange" />
<add extension=".js" policy="CacheUntilChange" />
</profiles>
</caching>
</system.webServer>
kernelCachePolicy:HTTP.sys 核心快取,效能最高,繞過 Managed Pipeline。<location path="Admin"> ... policy="DontCache" </location>Area:Entity:Action:ParamHash,避免衝突、易於清除。
if (Cache[key] == null)
{
lock (syncObj)
{
if (Cache[key] == null)
Cache.Insert(key, BuildExpensiveObject(), ...);
}
}
PerformanceCounter 類別 .NET CLR Memory / Cache。Cache 受限於 AppDomain、記憶體上限、單點故障。擴展至 Redis、NCache、SQL Server 分散式快取 時,介面抽象 ICacheProvider 平滑切換。| 反模式 | 後果 | 修正 |
|---|---|---|
| 快取巨大物件圖 ( > 1MB ) | LOH 碎片化、GC 壓力大 | 拆分細粒度、序列化壓縮、外部儲存 |
| 無過期策略永久駐留 | 記憶體洩漏、資料過期 | 強制 AbsoluteExpiration、依賴檔案/SQL |
| 快取 DbContext / Entity 實體 | 追蹤衝突、連線洩漏、序列化失敗 | 快取 DTO/POCO、AsNoTracking 查詢 |
| 依賴 Session 狀態做快取 | 擴展性差、Web Farm 同步問題 | 改用分散式快取 |