SQL Server 2005 引入 **Query Notification**:訂閱查詢結果變更通知,配合 `System.Web.Caching.SqlCacheDependency` 實現 **資料庫驅動的緩存失效**,替代傳統輪詢/時間過期策略。
-- 檢查
SELECT DATABASEPROPERTYEX('MyDB', 'IsBrokerEnabled'); -- 1=啟用, 0=未啟用
-- 啟用 (需獨佔存取,建議維護窗口執行)
ALTER DATABASE MyDB SET ENABLE_BROKER;
-- 若卡住:ALTER DATABASE MyDB SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE;
// Global.asax
void Application_Start() {
SqlDependency.Start(ConfigurationManager.ConnectionStrings["MyDB"].ConnectionString);
}
void Application_End() {
SqlDependency.Stop(ConfigurationManager.ConnectionStrings["MyDB"].ConnectionString);
}
using (var conn = new SqlConnection(connStr))
using (var cmd = new SqlCommand("SELECT Col1, Col2 FROM dbo.Table WHERE Status = 1", conn)) {
conn.Open();
var dep = new SqlCacheDependency(cmd);
var onRemove = new CacheItemRemovedCallback(RemovedCallback);
Cache.Insert("MyCacheKey", GetData(), dep,
Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration,
CacheItemPriority.Default, onRemove);
}
using (var conn = new SqlConnection(connStr))
using (var cmd = new SqlCommand("SELECT Col1 FROM dbo.Table WHERE Id > @Id", conn)) {
cmd.Parameters.AddWithValue("@Id", 100);
conn.Open();
var dep = new SqlDependency(cmd);
dep.OnChange += (sender, e) => {
if (e.Type == SqlNotificationType.Change) {
// 清除快取、重新載入、記錄日誌...
InvalidateCache();
}
};
// 必須執行命令啟動訂閱
using (var reader = cmd.ExecuteReader()) { /* 讀取或直接關閉 */ }
}
| 現象 | 可能原因 | 排查 |
|---|---|---|
| 完全不觸發通知 | Service Broker 未啟用 / 防火牆阻擋 4022 埠 / 查詢不符合限制 | SQL Profiler 追蹤 `Query Notification` 事件、`Broker:Conversation` 事件 |
| 頻繁誤觸發 / 剛建立即觸發 | 查詢結果集不穩定 / 統計資訊過期導致計畫變更 / 隔離等級問題 | 簡化查詢、更新統計、檢查隔離等級 |
| 資料變更但不觸發 | 變更未影響查詢結果集 / 交易未提交 / 觸發器中修改未觸發 | 確認變更符合查詢條件、檢查 `SqlNotificationInfo` |
| 記憶體洩漏 / 對話堆積 | 未呼叫 `SqlDependency.Stop` / 應用池回收未清理 | 確實在 `Application_End` 呼叫 Stop、監控 `sys.transmission_queue` |