c# 如何用lock解决缓存击穿

作者:UP技术控 时间:2022-12-03 14:27:55 

目录
  • 背景

  • 解决方案

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

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

  • 总结说明

    • 1、缓存中有数据,直接走下述代码就返回结果了

    • 2、缓存中没有数据,第1个进入的线程,获取锁并从数据库去取数据,没释放锁之前,其他并行进入的线程会等待,再重新去缓存取数据。这样就防止都去数据库重复取数据,重复往缓存中更新数据情况出现。

    • 3、取得每个 Key专有的 lock object;若同时有多个 thread要求相同资料,只会(到数据库)查第一次,剩下的从 cache读取。

背景

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

解决方案

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

来源:https://www.cnblogs.com/lyl6796910/p/14354503.html

标签:c#,lock,缓存击穿
0
投稿

猜你喜欢

  • C++ 前置声明详解及实例

    2023-02-28 10:39:46
  • 解决springboot项目不配置数据源启动报错问题

    2022-04-27 23:12:21
  • Java 通过API操作GraphQL

    2021-12-24 03:21:49
  • Android 使用 okhttp3和retrofit2 进行单文件和多文件上传

    2023-04-29 07:33:22
  • Dubbo扩展点SPI实践示例解析

    2021-12-14 12:56:45
  • Java编写网上超市购物结算功能程序

    2021-10-29 13:55:52
  • C语言实现哈夫曼编码

    2022-07-25 07:35:04
  • Spring-IOC容器中的常用注解与使用方法详解

    2021-05-26 23:37:45
  • Android 九宫格的实现方法

    2022-08-19 01:07:09
  • Android自定义WaveProgressView实现水波纹加载需求

    2021-07-29 02:06:00
  • Spring Boot整合Redis的完整步骤

    2023-06-03 03:21:56
  • Android学习笔记之ActionBar Item用法分析

    2022-03-12 00:18:59
  • Android开发应用中Broadcast Receiver组件详解

    2023-04-25 09:35:35
  • Mybatis返回插入的主键问题解决方案

    2023-05-06 02:58:03
  • Android NTP 时间同步机制详解

    2023-03-29 23:02:09
  • Android 8.0的缓存大小和缓存清理接口方法

    2023-11-09 16:47:06
  • 详解Android消息机制完整的执行流程

    2021-10-14 18:11:00
  • UGUI绘制多点连续的平滑曲线

    2022-01-16 06:22:45
  • Java多线程编程实战之模拟大量数据同步

    2023-09-02 21:15:59
  • 在springboot中实现个别bean懒加载的操作

    2023-11-25 09:44:11
  • asp之家 软件编程 m.aspxhome.com