當前位置: 妍妍網 > 碼農

巧用lock解決緩存擊穿的解決方案

2024-07-12碼農

背景

緩存擊穿是指緩存中沒有但資料庫中有的數據(一般是緩存時間到期),這時由於並行使用者特別多,同時讀緩存沒讀到數據,又同時去資料庫去取數據,引起資料庫壓力瞬間增大,造成過大壓力。

解決方案

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();