c# 如何用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个进入的线程,获取c#教程锁并从数据库去取数据,没释放锁之前,其他并行进入的线程会等待,再重新去缓存取数据。这样就防止都去数据库重复取数据,重复往缓存中更新数据情况出现。

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读取。
1
2
3
4
5
6
7
8
string cacheLockKey = string.Format(MemoryCacheLockObjectFormat, key);
      lock (CacheObject)
      {
        var lockObject = CacheObject[cacheLockKey];
        if (lockObject == null)
        {
          // 取得每個 Key專屬的 lock object;若同時有多個 thread要求相同資料,只會(到資料庫)查第一次,剩下的從 cache讀取
          lockObject = new object();

以上就是c# 如何用lock解决缓存击穿的详细内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值