背景
緩存擊穿是指緩存中沒有但資料庫中有的數據(一般是緩存時間到期),這時由於並行使用者特別多,同時讀緩存沒讀到數據,又同時去資料庫去取數據,引起資料庫壓力瞬間增大,造成過大壓力。
解決方案
1、 設定熱點數據永遠不過期。
2、加互斥鎖,互斥鎖參考程式碼如下:
2.1、根據key生成object()
privatestaticobjectGetMemoryCacheLockObject(string key)
{
string cacheLockKey = string.Format(MemoryCacheLockObjectFormat, key);
lock (CacheObject)
{
var lockObject = CacheObject[cacheLockKey];
if (lockObject == null)
{
// 取得每個 Key專屬的 lock object;若同時有多個 thread要求相同資料,只會(到資料庫)查第一次,剩下的從 cache讀取
lockObject = newobject();
CacheObject.Set(
cacheLockKey,
lockObject,
new System.Runtime.Caching.CacheItemPolicy()
{
AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(10)
}
);
}
return lockObject;
}
}
2.2、lock住GetMemoryCacheLockObject(key)
public T Get<T>(string key, Func<T> getDataWork, TimeSpan absoluteExpireTime, bool forceRefresh = false, bool returnCopy = true) where T : class
{
try
{
lock (GetMemoryCacheLockObject(key))
{
/*
System.ArgumentNullException: Value cannot be null.
at System.Threading.Monitor.Enter(Object obj)
at BQoolCommon.Helpers.Cache.MemoryCacheLayer.Get[T](String key, Func`1 getDataWork, TimeSpan absoluteExpireTime, Boolean forceRefresh, Boolean returnCopy) in D:\Source\BQoolCommon\BQoolCommon.Helpers\Cache\MemoryCacheLayer.cs:line 46
*/
T result = CacheObject[key] as T;
if (result != null && forceRefresh)
{// 是否清除Cache,強制重查
result = null;
}
if (result == null)
{
//執行取得資料的委派作業
result = getDataWork();
if (result != null)
{
Set(key, result, absoluteExpireTime);
}
}
if (returnCopy)
{
//複製一份新的參考
string serialize = JsonConvert.SerializeObject(result);
return JsonConvert.DeserializeObject<T>(serialize);
}
else
{
return result;
}
}
}
catch
{
return getDataWork();
}
}
總結說明
1、緩存中有數據,直接走下述程式碼就返回結果了
T result = CacheObject[key] as T;
2、緩存中沒有數據,第1個進入的執行緒,獲取鎖並從資料庫去取數據,沒釋放鎖之前,其他並列進入的執行緒會等待,再重新去緩存取數據。這樣就防止都去資料庫重復取數據,重復往緩存中更新數據情況出現。
try
{
lock (GetMemoryCacheLockObject(key))
{
/*
System.ArgumentNullException: Value cannot be null.
at System.Threading.Monitor.Enter(Object obj)
at BQoolCommon.Helpers.Cache.MemoryCacheLayer.Get[T](String key, Func`1 getDataWork, TimeSpan absoluteExpireTime, Boolean forceRefresh, Boolean returnCopy) in D:\Source\BQoolCommon\BQoolCommon.Helpers\Cache\MemoryCacheLayer.cs:line 46
*/
T result = CacheObject[key] as T;
3、取得每個 Key專有的 lock object;若同時有多個 thread要求相同資料,只會(到資料庫)查第一次,剩下的從 cache讀取。
string cacheLockKey = string.Format(MemoryCacheLockObjectFormat, key);
lock (CacheObject)
{
var lockObject = CacheObject[cacheLockKey];
if (lockObject == null)
{
// 取得每個 Key專屬的 lock object;若同時有多個 thread要求相同資料,只會(到資料庫)查第一次,剩下的從 cache讀取
lockObject = newobject();