巧用Lock解决缓存击穿的解决方案

数据库 其他数据库
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。

背景

缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。

解决方案

1、设置热点数据永远不过期。

2、加互斥锁,互斥锁参考代码如下:

2.1、根据key生成object()

private static object GetMemoryCacheLockObject(string key)
        {
            string cacheLockKey = string.Format(MemoryCacheLockObjectFormat, key);
            lock (CacheObject)
            {
                var lockObject = CacheObject[cacheLockKey];
                if (lockObject == null)
                {
                    // 取得每個 Key專屬的 lock object;若同時有多個 thread要求相同資料,只會(到資料庫)查第一次,剩下的從 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,強制重查
                        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 = new object();
责任编辑:武晓燕 来源: 程序员编程日记
相关推荐

2021-01-31 10:51:37

缓存lock数据

2018-11-12 11:12:46

2023-11-10 14:58:03

2020-03-05 09:09:18

缓存原因方案

2023-10-13 08:11:22

2022-03-08 00:07:51

缓存雪崩数据库

2019-10-08 16:05:19

Redis数据库系统

2023-07-19 07:51:43

Redis缓存高可用

2024-06-24 00:30:00

2023-10-30 07:56:46

Spring缓存

2018-12-03 12:17:27

Semptian解决方案

2012-05-27 16:21:31

IDC华为

2018-12-03 12:13:21

Mellanox解决方案

2018-12-03 11:59:42

Inventec解决方案

2018-12-03 12:26:30

YADRO解决方案

2011-11-30 13:08:55

企业防毒防毒方案拯救三

2016-03-13 17:58:57

2011-12-09 11:13:17

2009-12-23 21:06:47

统一通信多媒体联络中心平台华为

2010-12-21 17:28:58

点赞
收藏

51CTO技术栈公众号