巧用lock解決緩存擊穿的解決方案
本文轉(zhuǎn)載自微信公眾號(hào)「UP技術(shù)控」,作者conan5566 。轉(zhuǎn)載本文請(qǐng)聯(lián)系UP技術(shù)控公眾號(hào)。
背景
緩存擊穿是指緩存中沒(méi)有但數(shù)據(jù)庫(kù)中有的數(shù)據(jù)(一般是緩存時(shí)間到期),這時(shí)由于并發(fā)用戶特別多,同時(shí)讀緩存沒(méi)讀到數(shù)據(jù),又同時(shí)去數(shù)據(jù)庫(kù)去取數(shù)據(jù),引起數(shù)據(jù)庫(kù)壓力瞬間增大,造成過(guò)大壓力。
解決方案
1、設(shè)置熱點(diǎn)數(shù)據(jù)永遠(yuǎn)不過(guò)期。
2、加互斥鎖,互斥鎖參考代碼如下:
2.1、根據(jù)key生成object()
- private static object GetMemoryCacheLockObject(string key)
- {
- string cacheLockKey = string.Format(MemoryCacheLockObjectFormat, key);
- lock (CacheObject)
- {
- var lockObject = CacheObject[cacheLockKey];
- if (lockObject == null)
- {
- // 取得每個(gè) Key專屬的 lock object;若同時(shí)有多個(gè) thread要求相同資料,只會(huì)(到資料庫(kù))查第一次,剩下的從 cache讀取
- lockObject = new object();
- 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,強(qiáng)制重查
- result = null;
- }
- if (result == null)
- {
- //執(zhí)行取得資料的委派作業(yè)
- 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();
- }
- }
總結(jié)說(shuō)明
1、緩存中有數(shù)據(jù),直接走下述代碼就返回結(jié)果了
- T result = CacheObject[key] as T;
2、緩存中沒(méi)有數(shù)據(jù),第1個(gè)進(jìn)入的線程,獲取鎖并從數(shù)據(jù)庫(kù)去取數(shù)據(jù),沒(méi)釋放鎖之前,其他并行進(jìn)入的線程會(huì)等待,再重新去緩存取數(shù)據(jù)。這樣就防止都去數(shù)據(jù)庫(kù)重復(fù)取數(shù)據(jù),重復(fù)往緩存中更新數(shù)據(jù)情況出現(xiàn)。
- 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、取得每個(gè) Key專有的 lock object;若同時(shí)有多個(gè) thread要求相同資料,只會(huì)(到數(shù)據(jù)庫(kù))查第一次,剩下的從 cache讀取。
- string cacheLockKey = string.Format(MemoryCacheLockObjectFormat, key);
- lock (CacheObject)
- {
- var lockObject = CacheObject[cacheLockKey];
- if (lockObject == null)
- {
- // 取得每個(gè) Key專屬的 lock object;若同時(shí)有多個(gè) thread要求相同資料,只會(huì)(到資料庫(kù))查第一次,剩下的從 cache讀取
- lockObject = new object();






























