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

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

以上就是c# 如何用lock解决缓存击穿的详细内容,更多关于c# lock解决缓存击穿的资料请关注我们其它相关文章!

(0)

相关推荐

  • c#多线程中Lock()关键字的用法小结

    本文介绍C# lock关键字,C#提供了一个关键字lock,它可以把一段代码定义为互斥段(critical section),互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待. 每个线程都有自己的资源,但是代码区是共享的,即每个线程都可以执行相同的函数.这可能带来的问题就是几个线程同时执行一个函数,导致数据的混乱,产生不可预料的结果,因此我们必须避免这种情况的发生. 其中,lock是一种比较好用的简单的线程同步方式,它是通过为给定对象获取互斥锁来实现同步的.它可以保证当一个线程在关键

  • C#中lock死锁实例教程

    在c#中有个关键字lock,它的作用是锁定某一代码块,让同一时间只有一个线程访问该代码块,本文就来谈谈lock关键字的原理和其中应注意的几个问题: lock的使用原型是: lock(X) { //需要锁定的代码.... } 首先要明白为什么上面这段话能够锁定代码,其中的奥妙就是X这个对象,事实上X是任意一种引用类型,它在这儿起的作用就是任何线程执行到lock(X)时候,X需要独享才能运行下面的代码,若假定现在有3个线程A,B,C都执行到了lock(X)而ABC因为此时都占有X,这时ABC就要停下

  • C#中实现线程同步lock关键字的用法详解

    1. lock关键字保证一个代码块在执行的过程中不会受到其他线程的干扰,这是通过在该代码块的运行过程中对特定的对象加互斥锁来实现的. 2. lock关键字的参数必须是引用类型的对象.lock对基本数据类型如int,long等无效,因为它所作用的类型必须是对象.如果传入long类型数据,势必被转换为Int64结构类型,则加锁的是全新的对象引用.如果需要对它们进行互斥访问限制,可以使用System.Threading.Interlocked类提供的方法,这个类是提供原子操作的. 3. lock(th

  • 如何使用C#读写锁ReaderWriterLockSlim

    读写锁的概念很简单,允许多个线程同时获取读锁,但同一时间只允许一个线程获得写锁,因此也称作共享-独占锁.在C#中,推荐使用ReaderWriterLockSlim类来完成读写锁的功能. 某些场合下,对一个对象的读取次数远远大于修改次数,如果只是简单的用lock方式加锁,则会影响读取的效率.而如果采用读写锁,则多个线程可以同时读取该对象,只有等到对象被写入锁占用的时候,才会阻塞. 简单的说,当某个线程进入读取模式时,此时其他线程依然能进入读取模式,假设此时一个线程要进入写入模式,那么他不得不被阻塞

  • C#笔试题之同线程Lock语句递归不会死锁

    前几天在网上闲逛,无意中看到有这么一道题及其答案,如下: 根据线程安全的相关知识,分析以下代码,当调用test方法时i>10时是否会引起死锁?并简要说明理由. public void test(int i) { lock(this) { if (i > 10) { i--; test(i); } } } 答:不会发生死锁,(但有一点int是按值传递的,所以每次改变的都只是一个副本,因此不会出现死锁.但如果把int换做一个object,那么死锁会发生) 当我看到这道题时,我心里只有两个答案,1.

  • C#中使用Interlocked进行原子操作的技巧

    什么是原子操作? 原子(atom)本意是"不能被进一步分割的最小粒子",而原子操作(atomic operation)意为"不可被中断的一个或一系列操作" .在C#中有多个线程同时对某个变量进行操作的时候,我们应该使用原子操作,防止多线程取到的值不是最新的值. 例如:int result = 0; 多线程A正在执行 result(0)+1 多线程B同时执行 result(0)+1 那么最终result的结果是1还是2呢,这个就很难说了.如果在CPU中2个线程同时计算

  • 使用Golang的singleflight防止缓存击穿的方法

    在使用缓存时,容易发生缓存击穿. 缓存击穿:一个存在的key,在缓存过期的瞬间,同时有大量的请求过来,造成所有请求都去读dB,这些请求都会击穿到DB,造成瞬时DB请求量大.压力骤增. singleflight 介绍 import "golang.org/x/sync/singleflight" singleflight类的使用方法就新建一个singleflight.Group,使用其方法Do或者DoChan来包装方法,被包装的方法在对于同一个key,只会有一个协程执行,其他协程等待那个

  • C#中的lock、Monitor、Mutex学习笔记

    线程:线程是进程的独立执行单元,每一个进程都有一个主线程,除了主线程可以包含其他的线程. 多线程的意义:多线程有助于改善程序的总体响应性,提高CPU的效率. 多线程的应用程序域是相当不稳定的,因为多个线程在同一时间内都能运行共享的功能模块.为了保护应用程序的资源不被破坏,为多线程程序提供了三种加锁的机制,分别是:Monitor类.Lock关键字和Mutex类. 1. lock lock实现的功能是:使后进入的线程不会中断当前的线程,而是等待当前线程结束后再继续执行. 应用: 复制代码 代码如下:

  • C#中lock用法详解

    本文实例讲述了C#中lock的用法.分享给大家供大家参考.具体分析如下: lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断.这是通过在代码块运行期间为给定对象获取互斥锁来实现的. 先来看看执行过程,代码示例如下: lock 语句用于获取某个给定对象的互斥锁,执行一个语句,然后释放该锁. lock-statement:(lock 语句:) 复制代码 代码如下: lock(expression) embedded-statement(lock   (   表达式   )   嵌入语句

  • C#使用Interlocked实现线程同步

    通过System.Threading命名空间的Interlocked类控制计数器,从而实现进程 的同步.Iterlocked类的部分方法如下表: 示例,同时开启两个线程,一个写入数据,一个读出数据 代码如下:(但是运行结果却不是我们想象的那样) using System; using System.Threading; namespace 线程同步 { class Program { static void Main(string[] args) { //缓冲区,只能容纳一个字符 char bu

  • 一篇文章带你轻松了解C# Lock关键字

    相信绝大多数.NET玩家和我一样,常常使用Timer这个对象,而在WPF中使用DispatcherTimer的人也是很多,DispatcherTimer是在UI线程跑的.我们的程序中大多数都会充斥很多Timer,可以理解它是一个线程,它继承自 System.Windows.Threading . 程序中也许会有一些静态变量或是单例模式的对象来让不同的页面进行交互,但也就是这样让每个线程之间打架提供了基础.因为资源是单独的,就像是脚踩两只船的人,必定会翻车.例如一个List集合,你在一个线程中对它

随机推荐