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

共 4256字,需浏览 9分钟

 ·

2021-02-03 07:58


背景

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

解决方案

    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(string key, Func 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(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();



往期精彩回顾




【推荐】.NET Core开发实战视频课程 ★★★

.NET Core实战项目之CMS 第一章 入门篇-开篇及总体规划

【.NET Core微服务实战-统一身份认证】开篇及目录索引

Redis基本使用及百亿数据量中的使用技巧分享(附视频地址及观看指南)

.NET Core中的一个接口多种实现的依赖注入与动态选择看这篇就够了

10个小技巧助您写出高性能的ASP.NET Core代码

用abp vNext快速开发Quartz.NET定时任务管理界面

在ASP.NET Core中创建基于Quartz.NET托管服务轻松实现作业调度

现身说法:实际业务出发分析百亿数据量下的多表查询优化

关于C#异步编程你应该了解的几点建议

C#异步编程看这篇就够了


浏览 16
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报